问题
I have created a MVC WebRole Window Azure application where i upload encrypted files to Azure blob storage using SymmetricAlgorithm (Rijndael) like this
Controler>Action is
[HttpPost]
public ActionResult UploadImage_post(HttpPostedFileBase fileBase)
{
if (fileBase.ContentLength > 0)
{
// Retrieve a reference to a container
Microsoft.WindowsAzure.StorageClient.CloudBlobContainer blobContainer =
_myBlobStorageService.GetCloudBlobContainer();
Microsoft.WindowsAzure.StorageClient.CloudBlob blob =
blobContainer.GetBlobReference(fileBase.FileName);
using (BlobStream blobStream = blob.OpenWrite())
{
string encryptionKey = //somekey;
byte[] file = new byte[fileBase.ContentLength];
EncDecAlgo.EncryptBlobFile(file, blobStream, encryptionKey);
}
}
}
public void EncryptBlobFile(byte[] file, BlobStream bs, string key)
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(key,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
Rijndael alg = Rijndael.Create();
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
CryptoStream cs = new CryptoStream(bs,
alg.CreateEncryptor(), CryptoStreamMode.Write);
foreach (var data in file)
{
cs.WriteByte((byte)data);
}
cs.Close();
bs.Close();
}
The above File encryption is working fine.
For Downloading code is
public ActionResult DownloadFile(string filename)
{
// Retrieve reference to a previously created container.
Microsoft.WindowsAzure.StorageClient.CloudBlobContainer blobContainer =
_myBlobStorageService.GetCloudBlobContainer();
Microsoft.WindowsAzure.StorageClient.CloudBlob blob =
blobContainer.GetBlobReference(filename);
blob.FetchAttributes();
string encryptionKey = //same key used in encryption;
using (BlobStream blobStream = blob.OpenRead())
{
EncDecAlgo.DecryptBlobFile(blobStream, encryptionKey, filename);
}
}
public static void DecryptBlobFile(BlobStream bs, string key, string filePath)
{
try
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(key,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65,
0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
Rijndael alg = Rijndael.Create();
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
CryptoStream cs = new CryptoStream(bs,
alg.CreateDecryptor(), CryptoStreamMode.Read);
// Decrypt & Download Here
System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(filePath));
System.Web.HttpContext.Current.Response.ContentType = "application/" + Path.GetExtension(filePath).Replace(".", "");
int data;
while ((data = cs.ReadByte()) != -1)
{
if (data != 0)
{
}
System.Web.HttpContext.Current.Response.OutputStream.WriteByte((byte)data);
System.Web.HttpContext.Current.Response.Flush();
}
cs.Close();
bs.Close();
}
catch
{
}
}
On downloading get following error
Server cannot set content type after HTTP headers have been sent.
Please suggest some solution.
回答1:
This should be fairly simple, hope this is enough to get you started:
public class CloudFileResult : ActionResult
{
private string m_FileName;
private CloudBlobContainer m_Container;
public CloudFileResult(string imageName, CloudBlobContainer container)
{
if (string.IsNullOrEmpty(imageName))
{
throw new ArgumentNullException("imageName");
}
if (container == null)
{
throw new ArgumentNullException("container");
}
m_FileName = imageName;
m_Container = container;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
var blockBlob = m_Container.GetBlockBlobReference(m_FileName);
blockBlob.FetchAttributes();
context.HttpContext.Response.ContentType = blockBlob.Metadata["ContentType"];
const string key = "my secret";
using (var pdb = new Rfc2898DeriveBytes(key, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }))
{
using (var alg = RijndaelManaged.Create())
{
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
using (var stream = new CryptoStream(context.HttpContext.Response.OutputStream, alg.CreateDecryptor(), CryptoStreamMode.Write))
{
blockBlob.DownloadToStream(stream);
}
}
}
}
}
static void UploadFileToCloud(CloudBlobContainer container, HttpPostedFileBase file)
{
const string key = "my secret";
using (var pdb = new Rfc2898DeriveBytes(key, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }))
{
using (var alg = RijndaelManaged.Create())
{
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
var blockBlob = container.GetBlockBlobReference(file.FileName);
using (var stream = new CryptoStream(file.InputStream, alg.CreateEncryptor(), CryptoStreamMode.Read))
{
blockBlob.UploadFromStream(stream);
}
blockBlob.Metadata.Add("ContentType", file.ContentType);
blockBlob.SetMetadata();
}
}
}
static CloudBlobContainer GetContainer()
{
string connection = "DefaultEndpointsProtocol=http;AccountName=AzureAccount;AccountKey=AzureAccountKey;";
var account = CloudStorageAccount.Parse(connection);
var client = account.CreateCloudBlobClient();
var container = client.GetContainerReference("container");
return container;
}
As for download you can simple use:
[HttpGet]
public ActionResult Index(string fileName)
{
if (!string.IsNullOrEmpty(fileName))
{
return new CloudFileResult(fileName, GetContainer());
}
return View();
}
Pointers:
- I prefer using managed crypto algorithms
- I store contenttype of original file in blob metadata (so you know how to serve it)
- catch {} gives me creeps, at least log the exception somewhere
- Rather than playing with HttpContext.Response, create custom ActionResult
- Always dispose IDisposable stuff
来源:https://stackoverflow.com/questions/23744500/downloading-encrypted-file-from-window-azure-storage