Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ ServiceStack.Azure includes implementation of the following ServiceStack provide

- [ServiceBusMqServer](#ServiceBusMqServer) - [MQ Server](http://docs.servicestack.net/messaging) for invoking ServiceStack Services via Azure ServiceBus
- [AzureBlobVirtualFiles](#virtual-filesystem-backed-by-azure-blob-storage) - Virtual file system based on Azure Blob Storage
- [AzureAppendBlobVirtualFiles](#virtual-filesystem-backed-by-azure-blob-storage) - Virtual file system based on Azure Blob Storage for appending scenarios
- [AzureTableCacheClient](#caching-support-with-azure-table-storage) - Cache client over Azure Table Storage


Expand Down Expand Up @@ -50,6 +51,26 @@ public class AppHost : AppHostBase
}
```

In addition you can use **AzureAppendBlobVirtualFiles** in scenarios that require appending such as logging.

```csharp
public class AppHost : AppHostBase
{
public override void Configure(Container container)
{
Plugins.Add(new RequestLogsFeature
{
RequestLogger = new CsvRequestLogger(
files: new AzureAppendBlobVirtualFiles(AppSettings.Get<string>("storageConnection"), "logfiles"),
requestLogsPattern: "requestlogs/{year}-{month}/{year}-{month}-{day}.csv",
errorLogsPattern: "requestlogs/{year}-{month}/{year}-{month}-{day}-errors.csv",
appendEvery: TimeSpan.FromSeconds(30))

});
}
}
```

## Caching support with Azure Table Storage

The AzureTableCacheClient implements [ICacheClientExteded](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Caching/ICacheClientExtended.cs) and [IRemoveByPattern](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Caching/IRemoveByPattern.cs) using Azure Table Storage.
Expand Down
111 changes: 111 additions & 0 deletions src/ServiceStack.Azure/Storage/AzureAppendBlobVirtualDirectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using ServiceStack.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using Microsoft.WindowsAzure.Storage.Blob;
using ServiceStack.VirtualPath;

namespace ServiceStack.Azure.Storage
{
public class AzureAppendBlobVirtualDirectory : AbstractVirtualDirectoryBase
{
private readonly AzureAppendBlobVirtualFiles pathProvider;

public AzureAppendBlobVirtualDirectory(AzureAppendBlobVirtualFiles pathProvider, string directoryPath)
: base(pathProvider)
{
this.pathProvider = pathProvider;
this.DirectoryPath = directoryPath;

if (directoryPath == "/" || directoryPath.IsNullOrEmpty())
return;

var separatorIndex = directoryPath.LastIndexOf(pathProvider.RealPathSeparator, StringComparison.Ordinal);

ParentDirectory = new AzureAppendBlobVirtualDirectory(pathProvider,
separatorIndex == -1 ? string.Empty : directoryPath.Substring(0, separatorIndex));
}

public string DirectoryPath { get; set; }

public override IEnumerable<IVirtualDirectory> Directories
{
get
{
var blobs = pathProvider.Container.ListBlobs(DirectoryPath == null
? null
: DirectoryPath + pathProvider.RealPathSeparator);

return blobs.Where(q => q.GetType() == typeof(CloudBlobDirectory))
.Select(q =>
{
var blobDir = (CloudBlobDirectory)q;
return new AzureAppendBlobVirtualDirectory(pathProvider, blobDir.Prefix.Trim(pathProvider.RealPathSeparator[0]));
});
}
}

public override DateTime LastModified
{
get
{
throw new NotImplementedException();
}
}

public override IEnumerable<IVirtualFile> Files => pathProvider.GetImmediateFiles(this.DirectoryPath);

// Azure Blob storage directories only exist if there are contents beneath them
public bool Exists()
{
var ret = pathProvider.Container.ListBlobs(this.DirectoryPath, false)
.Where(q => q.GetType() == typeof(CloudBlobDirectory))
.Any();
return ret;

}

public override string Name => DirectoryPath?.SplitOnLast(pathProvider.RealPathSeparator).Last();

public override string VirtualPath => DirectoryPath;

public override IEnumerator<IVirtualNode> GetEnumerator()
{
throw new NotImplementedException();
}

protected override IVirtualFile GetFileFromBackingDirectoryOrDefault(string fileName)
{
fileName = pathProvider.CombineVirtualPath(this.DirectoryPath, pathProvider.SanitizePath(fileName));
return pathProvider.GetFile(fileName);
}

protected override IEnumerable<IVirtualFile> GetMatchingFilesInDir(string globPattern)
{
var dir = (this.DirectoryPath == null) ? null : this.DirectoryPath + pathProvider.RealPathSeparator;

var ret = pathProvider.Container.ListBlobs(dir)
.Where(q => q.GetType() == typeof(CloudAppendBlob))
.Where(q =>
{
var x = ((CloudAppendBlob)q).Name.Glob(globPattern);
return x;
})
.Select(q =>
{
return new AzureAppendBlobVirtualFile(pathProvider, this).Init(q as CloudAppendBlob);
});
return ret;
}

protected override IVirtualDirectory GetDirectoryFromBackingDirectoryOrDefault(string directoryName)
{
return new AzureAppendBlobVirtualDirectory(this.pathProvider, pathProvider.SanitizePath(DirectoryPath.CombineWith(directoryName)));
}


}
}
61 changes: 61 additions & 0 deletions src/ServiceStack.Azure/Storage/AzureAppendBlobVirtualFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using ServiceStack.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using ServiceStack.VirtualPath;
using Microsoft.WindowsAzure.Storage.Blob;

namespace ServiceStack.Azure.Storage
{
public class AzureAppendBlobVirtualFile : AbstractVirtualFileBase
{

private readonly AzureAppendBlobVirtualFiles pathProvider;
private readonly CloudBlobContainer container;

public CloudAppendBlob Blob { get; private set; }

public AzureAppendBlobVirtualFile(AzureAppendBlobVirtualFiles owningProvider, IVirtualDirectory directory)
: base(owningProvider, directory)
{
this.pathProvider = owningProvider;
this.container = pathProvider.Container;
}

public AzureAppendBlobVirtualFile Init(CloudAppendBlob blob)
{
this.Blob = blob;
return this;
}

public override DateTime LastModified => Blob.Properties.LastModified?.UtcDateTime ?? DateTime.MinValue;

public override long Length => Blob.Properties.Length;

public override string Name => Blob.Name.Contains(pathProvider.VirtualPathSeparator)
? Blob.Name.SplitOnLast(pathProvider.VirtualPathSeparator)[1]
: Blob.Name;

public string FilePath => Blob.Name;

public string ContentType => Blob.Properties.ContentType;

public override string VirtualPath => FilePath;

public override Stream OpenRead()
{
return Blob.OpenRead();
}

public override void Refresh()
{
CloudAppendBlob blob = pathProvider.Container.GetAppendBlobReference(Blob.Name);
if (!blob.Exists()) return;

Init(blob);
}
}
}
157 changes: 157 additions & 0 deletions src/ServiceStack.Azure/Storage/AzureAppendBlobVirtualFiles.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using ServiceStack.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using ServiceStack.VirtualPath;
using Microsoft.WindowsAzure.Storage.RetryPolicies;

namespace ServiceStack.Azure.Storage
{
public class AzureAppendBlobVirtualFiles : AbstractVirtualPathProviderBase, IVirtualFiles
{
public CloudBlobContainer Container { get; }

private readonly AzureAppendBlobVirtualDirectory rootDirectory;

public override IVirtualDirectory RootDirectory => rootDirectory;

public override string VirtualPathSeparator => "/";

public override string RealPathSeparator => "/";

public AzureAppendBlobVirtualFiles(string connectionString, string containerName)
{
var storageAccount = CloudStorageAccount.Parse(connectionString);

//containerName is the name of Azure Storage Blob container
Container = storageAccount.CreateCloudBlobClient().GetContainerReference(containerName);
Container.CreateIfNotExists();
rootDirectory = new AzureAppendBlobVirtualDirectory(this, null);
}


public AzureAppendBlobVirtualFiles(CloudBlobContainer container)
{
Container = container;
Container.CreateIfNotExists();
rootDirectory = new AzureAppendBlobVirtualDirectory(this, null);
}

protected override void Initialize()
{
}

public void WriteFile(string filePath, string textContents)
{
var blob = Container.GetAppendBlobReference(SanitizePath(filePath));
blob.CreateOrReplace(null,null,null);
blob.Properties.ContentType = MimeTypes.GetMimeType(filePath);
blob.AppendText(textContents);
}

public void WriteFile(string filePath, Stream stream)
{
var blob = Container.GetAppendBlobReference(SanitizePath(filePath));
blob.CreateOrReplace(AccessCondition.GenerateEmptyCondition(), new BlobRequestOptions() { RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(1), 10) }, null);
blob.Properties.ContentType = MimeTypes.GetMimeType(filePath);
blob.AppendFromStream(stream);
}

public void WriteFiles(IEnumerable<IVirtualFile> files, Func<IVirtualFile, string> toPath = null)
{
this.CopyFrom(files, toPath);
}

public void AppendFile(string filePath, string textContents)
{
var blob = Container.GetAppendBlobReference(SanitizePath(filePath));
blob.AppendText(textContents);

}

public void AppendFile(string filePath, Stream stream)
{
var blob = Container.GetAppendBlobReference(SanitizePath(filePath));
blob.AppendFromStream(stream);
}

public void DeleteFile(string filePath)
{
var blob = Container.GetAppendBlobReference(SanitizePath(filePath));
blob.Delete();
}

public void DeleteFiles(IEnumerable<string> filePaths)
{
filePaths.Each(DeleteFile);
}

public void DeleteFolder(string dirPath)
{
dirPath = SanitizePath(dirPath);
// Delete based on a wildcard search of the directory
if (!dirPath.EndsWith("/")) dirPath += "/";
//directoryPath += "*";
foreach (var blob in Container.ListBlobs(dirPath, true))
{
Container.GetAppendBlobReference(((CloudAppendBlob)blob).Name).DeleteIfExists();
}
}

public override IVirtualFile GetFile(string virtualPath)
{
var filePath = SanitizePath(virtualPath);

CloudAppendBlob blob = Container.GetAppendBlobReference(filePath);
if (!blob.Exists()) return null;

return new AzureAppendBlobVirtualFile(this, GetDirectory(GetDirPath(virtualPath))).Init(blob);
}

public override IVirtualDirectory GetDirectory(string virtualPath)
{
return new AzureAppendBlobVirtualDirectory(this, virtualPath);
}

public override bool DirectoryExists(string virtualPath)
{
var ret = ((AzureAppendBlobVirtualDirectory)GetDirectory(virtualPath)).Exists();
return ret;
}

public string GetDirPath(string filePath)
{
if (string.IsNullOrEmpty(filePath))
return null;

var lastDirPos = filePath.LastIndexOf(VirtualPathSeparator[0]);
return lastDirPos >= 0
? filePath.Substring(0, lastDirPos)
: null;
}

public IEnumerable<AzureAppendBlobVirtualFile> GetImmediateFiles(string fromDirPath)
{
var dir = new AzureAppendBlobVirtualDirectory(this, fromDirPath);

return Container.ListBlobs((fromDirPath == null) ? null : fromDirPath + this.RealPathSeparator)
.Where(q => q.GetType() == typeof(CloudAppendBlob))
.Select(q => new AzureAppendBlobVirtualFile(this, dir).Init(q as CloudAppendBlob));

}

public string SanitizePath(string filePath)
{
var sanitizedPath = string.IsNullOrEmpty(filePath)
? null
: (filePath[0] == VirtualPathSeparator[0] ? filePath.Substring(1) : filePath);

return sanitizedPath != null
? sanitizedPath.Replace('\\', VirtualPathSeparator[0])
: null;
}
}
}
Loading