Created
April 6, 2018 12:49
-
-
Save tusmester/af62e7be1d29320542c3948ef6512ee2 to your computer and use it in GitHub Desktop.
#sn #blob #provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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