Загрузка файлов BLOB-объектов Azure в MVC3 - PullRequest
31 голосов
/ 19 июля 2011

Наше приложение ASP.NET MVC 3 работает в Azure и использует Blob в качестве хранилища файлов. Я разобрался с частью загрузки.

Представление будет иметь имя файла, которое при щелчке предложит отобразить экран загрузки файла.

Может кто-нибудь сказать мне, как это сделать?

Ответы [ 3 ]

59 голосов
/ 20 июля 2011

На самом деле два варианта ... первый - просто перенаправить пользователя к большому объекту (если большие объекты находятся в общедоступном контейнере). Это будет выглядеть примерно так:

return Redirect(container.GetBlobReference(name).Uri.AbsoluteUri);

Если большой двоичный объект находится в закрытом контейнере, вы можете либо использовать подпись общего доступа и выполнить перенаправление, как в предыдущем примере, либо вы можете прочитать большой двоичный объект в действии контроллера и передать его клиенту в качестве загрузки:

Response.AddHeader("Content-Disposition", "attachment; filename=" + name); // force download
container.GetBlobReference(name).DownloadToStream(Response.OutputStream);
return new EmptyResult();
10 голосов
/ 24 марта 2016

Я заметил, что запись в поток ответов из метода action портит заголовки HTTP. Некоторые ожидаемые заголовки отсутствуют, а другие установлены неправильно.

Таким образом, вместо записи в поток ответов, я получаю содержимое BLOB-объекта в виде потока и передаю его методу Controller.File ().

CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
Stream blobStream = blob.OpenRead();
return File(blobStream, blob.Properties.ContentType, "FileName.txt");
9 голосов
/ 04 мая 2015

Вот возобновляемая версия (полезная для больших файлов или позволяющая искать при воспроизведении видео или аудио) доступа к частному блобу:

public class AzureBlobStream : ActionResult
{
    private string filename, containerName;

    public AzureBlobStream(string containerName, string filename)
    {
        this.containerName = containerName;
        this.filename = filename;
        this.contentType = contentType;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var response = context.HttpContext.Response;
        var request = context.HttpContext.Request;

        var connectionString = ConfigurationManager.ConnectionStrings["Storage"].ConnectionString;
        var account = CloudStorageAccount.Parse(connectionString);
        var client = account.CreateCloudBlobClient();
        var container = client.GetContainerReference(containerName);
        var blob = container.GetBlockBlobReference(filename);

        blob.FetchAttributes();
        var fileLength = blob.Properties.Length;
        var fileExists = fileLength > 0;
        var etag = blob.Properties.ETag;

        var responseLength = fileLength;
        var buffer = new byte[4096];
        var startIndex = 0;

        //if the "If-Match" exists and is different to etag (or is equal to any "*" with no resource) then return 412 precondition failed
        if (request.Headers["If-Match"] == "*" && !fileExists ||
            request.Headers["If-Match"] != null && request.Headers["If-Match"] != "*" && request.Headers["If-Match"] != etag)
        {
            response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
            return;
        }

        if (!fileExists)
        {
            response.StatusCode = (int)HttpStatusCode.NotFound;
            return;
        }

        if (request.Headers["If-None-Match"] == etag)
        {
            response.StatusCode = (int)HttpStatusCode.NotModified;
            return;
        }

        if (request.Headers["Range"] != null && (request.Headers["If-Range"] == null || request.Headers["IF-Range"] == etag))
        {
            var match = Regex.Match(request.Headers["Range"], @"bytes=(\d*)-(\d*)");
            startIndex = Util.Parse<int>(match.Groups[1].Value);
            responseLength = (Util.Parse<int?>(match.Groups[2].Value) + 1 ?? fileLength) - startIndex;
            response.StatusCode = (int)HttpStatusCode.PartialContent;
            response.Headers["Content-Range"] = "bytes " + startIndex + "-" + (startIndex + responseLength - 1) + "/" + fileLength;
        }

        response.Headers["Accept-Ranges"] = "bytes";
        response.Headers["Content-Length"] = responseLength.ToString();
        response.Cache.SetCacheability(HttpCacheability.Public); //required for etag output
        response.Cache.SetETag(etag); //required for IE9 resumable downloads
        response.ContentType = blob.Properties.ContentType;

        blob.DownloadRangeToStream(response.OutputStream, startIndex, responseLength);
    }
}

Пример:

Response.AddHeader("Content-Disposition", "attachment; filename=" + filename); // force download
return new AzureBlobStream(blobContainerName, filename);
...