Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tusmester/af62e7be1d29320542c3948ef6512ee2 to your computer and use it in GitHub Desktop.
Save tusmester/af62e7be1d29320542c3948ef6512ee2 to your computer and use it in GitHub Desktop.
#sn #blob #provider
using SenseNet.ContentRepository.Storage.Data.SqlClient.Blob;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using SenseNet.Diagnostics;
namespace SenseNet.ContentRepository.Tests.Data
{
class LocalDiskChunkBlobProvider : IBlobProvider
{
private static StringBuilder _trace = new StringBuilder();
public static string Trace { get { return _trace.ToString(); } }
private static int _chunkByteSize = 10; //ten bytes by default
public static int ChunkByteSize { get;set; }
internal class LocalDiskChunkBlobProviderData
{
public Guid Id { get; set; }
public string Trace { get; set; }
public int ChunkSize { get; set; }
}
private static string _rootDirectory;
public LocalDiskChunkBlobProvider()
{
_rootDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data\\" + GetType().Name);
if (Directory.Exists(_rootDirectory))
{
foreach (var path in Directory.GetFiles(_rootDirectory))
System.IO.File.Delete(path);
}
else
{
Directory.CreateDirectory(_rootDirectory);
}
}
public static void _ClearTrace()
{
_trace.Clear();
}
/*========================================================================== provider implementation */
public object ParseData(string providerData)
{
return BlobStorageContext.DeserializeBlobProviderData<LocalDiskChunkBlobProviderData>(providerData);
}
public void Allocate(BlobStorageContext context)
{
//Create a guaranteed unique identifier that will
//be used as the name of the new folder to store the chunks
var id = Guid.NewGuid();
//Pass the created id to the method that creates the actual
//folder for the chunks
CreateFolder(id);
//Initialize the BlobProviderData with the unique id and the
//set chunk size
context.BlobProviderData = new LocalDiskChunkBlobProviderData { Id = id, ChunkSize = _chunkByteSize };
//Optionally, make a note of this operation in the log
SnTrace.Test.Write("LocalDiskChunkBlobProvider.Allocate: " + id);
}
public void Delete(BlobStorageContext context)
{
//By casting the BlobProviderData with the right type,
//we can acquire the id that is needed for the delete operation
var id = ((LocalDiskChunkBlobProviderData)context.BlobProviderData).Id;
//Call the delete method and pass the relevant id to remove all
//unnecessary consituents of the previously stored content
DeleteFolder(id);
//Optionally, make a note of this operation in the log
SnTrace.Test.Write("LocalDiskChunkBlobProvider.Delete: " + id);
}
public Stream GetStreamForRead(BlobStorageContext context)
{
//By casting the BlobProviderData, arriving with the context,
//with the right type, we can create the required input
//parameter for the following method call
var providerData = (LocalDiskChunkBlobProviderData)context.BlobProviderData;
//Retrieve and return the read-only stream assembled from
//all chunks. Necessary parameters can be extracted from the context.
return new FileSystemChunkReaderStream(providerData, context.Length, GetDirectoryPath(providerData.Id));
}
public Stream GetStreamForWrite(BlobStorageContext context)
{
//By casting the BlobProviderData, arriving with the context,
//with the right type, we can create the required input
//parameter for the following method call
var providerData = (LocalDiskChunkBlobProviderData)context.BlobProviderData;
//Retrieve and return the writable stream assembled from all
//chunks. Necessary parameters can be extracted from the context.
return new FileSystemChunkWriterStream(providerData, context.Length, GetDirectoryPath(providerData.Id));
}
public Stream CloneStream(BlobStorageContext context, Stream stream)
{
//Optionally, make a note of this operation in the log
SnTrace.Test.Write("LocalDiskChunkBlobProvider.CloneStream: {0}", context.FileId);
//Examine the incoming stream against the required type.
//If it is not a match then throw an exception
if (!(stream is FileSystemChunkReaderStream))
throw new InvalidOperationException("Stream must be a FileSystemChunkReaderStream in the local disk provider.");
//By taking advantage of already existing logic,
//call GetStreamForRead with the incoming context
return GetStreamForRead(context);
}
public void Write(BlobStorageContext context, long offset, byte[] buffer)
{
//By casting the BlobProviderData, arriving with the
//context, with the right type, we can create the
//providerdata and use its fields and properties
//to retrieve necessary data
var providerData = (LocalDiskChunkBlobProviderData)context.BlobProviderData;
//Set the appropriate id so chunks will be written
//in the right folder
var originalFileId = providerData.Id;
//Set appropriate chunk size as, inside a particular
//folder, chunks must have the same length
var originalChunkSize = providerData.ChunkSize;
//Determine the length of the incoming binary
var currentBlobSize = context.Length;
//Check if the incoming offset (the point of start of write)
//and the length of the binary data to be written are
//consistent with settings (e.g., one may not start writing
//content mid-chunk)
AssertValidChunks(currentBlobSize, originalChunkSize, offset, buffer.Length);
//The following piece of code takes apart the incoming
//byte array according to the set chunk size and writes
//every chunk into a separate file
var length = buffer.Length;
var sourceOffset = 0;
while (length > 0)
{
var chunkIndex = Convert.ToInt32( (offset / originalChunkSize) );
var currentChunkLength = Math.Min(originalChunkSize, length);
var bytes = new byte[currentChunkLength];
Array.ConstrainedCopy(buffer, sourceOffset, bytes, 0, currentChunkLength);
WriteChunk(((LocalDiskChunkBlobProviderData)context.BlobProviderData).Id, chunkIndex, bytes);
length -= bytes.Length;
offset += originalChunkSize;
sourceOffset += originalChunkSize;
}
//Optionally, make a note of this operation in the log
SnTrace.Test.Write("LocalDiskChunkBlobProvider.Write");
}
/*================================================================================== utility methods */
private static string GetDirectoryPath(Guid id)
{
//Concatenate the root folder name with the incoming
//id in order to return a complete path
return Path.Combine(_rootDirectory, id.ToString());
}
private static string GetFilePath(Guid id, int chunkIndex)
{
//Concatenate the complete path with the incoming
//chunkIndex in order to return the full path of the chunk file
return Path.Combine(GetDirectoryPath(id), chunkIndex.ToString());
}
public static void WriteChunk(Guid id, int chunkIndex, byte[] bytes)
{
//Use existing logic to write chunk
using (var stream = new FileStream(GetFilePath(id, chunkIndex), FileMode.OpenOrCreate))
stream.Write(bytes, 0, bytes.Length);
}
private void CreateFolder(Guid id)
{
//Create target folder for the new content using the
//incoming id (that is based on a guaranteed unique
//identifier, or GUID)
Directory.CreateDirectory(GetDirectoryPath(id));
}
private void AssertValidChunks(long currentBlobSize, int chunkSize, long offset, int size)
{
//Make sure there is no remainder when dividing
//the offset (start of write) with the set chunk
//size. If there is, the operation is illegal,
//as write may not occur mid-chunk
if (offset % chunkSize > 0)
throw new Exception("Invalid offset");
}
private void DeleteFolder(Guid id)
{
//Retrieve the full directory path using the incoming id
var myPath = GetDirectoryPath(id);
//Only do the operation if the folder actually exists
if (System.IO.Directory.Exists(myPath))
{
System.IO.DirectoryInfo dirinfo = new DirectoryInfo(myPath);
//List and then remove all files under the folder to be
//deleted before attempting to remove the folder itself
foreach (FileInfo file in dirinfo.GetFiles())
{
file.Delete();
}
//Finally, remove the folder
foreach (DirectoryInfo dir in dirinfo.GetDirectories())
{
dir.Delete(true);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment