Подтвердить что ты не робот

Как загрузить огромные файлы в Azure blob с веб-страницы

Внутренне ASP.NET имеет адресное пространство объемом 2 ГБ, но на самом деле у вас есть только 1 ГБ для загрузки (см. http://support.microsoft.com/?id=295626). Кроме того, IIS 7 имеет ограничение в 30 МБ (см. http://www.iislogs.com/steveschofield/iis7-post-40-adjusting-file-upload-size-in-iis7), и вы предположительно должны запускать

appcmd set config "My Site/MyApp" -section:requestFiltering -requestLimits.maxAllowedContentLength:104857600 -commitpath:apphost

на сервере, чтобы выйти за пределы этого предела 30 МБ. Но как я могу запустить это на своих серверах Azure?

Кроме того, согласно http://support.microsoft.com/?id=295626

Во время процесса загрузки ASP.NET загружает весь файл в память до пользователь может сохранить файл в диск.

поэтому я быстро исчерпаю ограничение памяти, если многие пользователи одновременно загружают большие файлы. В моем коде ниже я использую потоки, но я предполагаю, что весь файл сначала загружается в память. Это тот случай?

using System;
using System.Web.Security;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace WebPages
{
    public partial class Upload : System.Web.UI.Page
    {
        CloudBlobClient BlobClient = null;
        CloudBlobContainer BlobContainer = null;

        void InitBlob()
        {
            // Setup the connection to Windows Azure Storage
            var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
            BlobClient = storageAccount.CreateCloudBlobClient();

            // Get and create the container
            BlobContainer = BlobClient.GetContainerReference("publicfiles");
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            //if (Membership.GetUser() == null) return;   // Only allow registered users to upload files

            InitBlob();

            try
            {
                var file = Request.Files["Filedata"];

                var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
                BlobClient = storageAccount.CreateCloudBlobClient();

                // Make a unique blob name
                var extension = System.IO.Path.GetExtension(file.FileName);

                // Create the Blob and upload the file
                var blobAddressUri = String.Format("{0}{1}", Guid.NewGuid(), extension);
                var blob = BlobContainer.GetBlobReference(blobAddressUri);

                blob.UploadFromStream(file.InputStream);

                // Set the metadata into the blob
                blob.Metadata["FileName"] = file.FileName;
                //blob.Metadata["Submitter"] = Membership.GetUser().UserName;
                blob.Metadata["Type"] = "Video";
                blob.Metadata["Description"] = "Test";
                blob.SetMetadata();

                // Set the properties
                blob.Properties.ContentType = file.ContentType;
                blob.SetProperties();
            }
            catch(Exception ex)
            {
                System.Diagnostics.Trace.TraceError("Upload file exception: {0}", ex.ToString());
                // If any kind of error occurs return a 500 Internal Server error
                Response.StatusCode = 500;
                Response.Write("An error occured while uploading the file");
                Response.End();
            }
        }
    }
}

Мне известны инструменты загрузки веб-страниц, такие как http://azureblobuploader.codeplex.com/, но мне действительно нужно, чтобы это было загружено с веб-страницы.

Итак, мои вопросы:

  • Как загрузить файлы в блоб, размер которых превышает 2 ГБ с веб-страницы.
  • Как я могу загружать файлы с веб-страницы в виде потока, который не содержит всю память.
  • Если решение должно написать мой собственный HttpModule или HttpHandler для обработки моей загрузки, как я могу установить это на своих серверах Azure? Можно ли использовать HttpHandlers как http://neatupload.codeplex.com/ на Azure?
  • Этот проект не для SharePoint, но я знаю, что в SharePoint у вас есть что-то, называемое провайдером Blob, и что вы можете написать свой собственный, есть ли поставщики Blob для ASP.NET?

Я также могу упомянуть, что мой код выше отлично работает по умолчанию с файлами размером менее 30 МБ, я использую SWFUpload V2.2.0 на клиенте.

Обновление 19. Июнь 19:09: @YvesGoeleven в Twitter дал мне подсказку с использованием общей подписки доступа (см. Msdn.microsoft.com/en-us/library/ee395415.aspx) и загрузку файла непосредственно в хранилище Azure Blob без прохождения через ASP.NET вообще, Я создал JSON WCF, который возвращает действительный SAS ut для моего хранилища blob.

using System.ServiceModel;
using System.ServiceModel.Web;

namespace WebPages.Interfaces
{
    [ServiceContract]
    public interface IUpload
    {
        [OperationContract]
        [WebInvoke(Method = "GET",
            ResponseFormat = WebMessageFormat.Json)]
        string GetUploadUrl();
    }
}

--------

using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.ServiceModel.Activation;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace WebPages.Interfaces
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class UploadService : IUpload
    {
        CloudBlobClient BlobClient;
        CloudBlobContainer BlobContainer;

        public UploadService()
        {
            // Setup the connection to Windows Azure Storage
            var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
            BlobClient = storageAccount.CreateCloudBlobClient();

            // Get and create the container
            BlobContainer = BlobClient.GetContainerReference("publicfiles");
        }

        string JsonSerialize(string url)
        {
            var serializer = new DataContractJsonSerializer(url.GetType());
            var memoryStream = new MemoryStream();

            serializer.WriteObject(memoryStream, url);

            return Encoding.Default.GetString(memoryStream.ToArray());
        }

        public string GetUploadUrl()
        {
            var sasWithIdentifier = BlobContainer.GetSharedAccessSignature(new SharedAccessPolicy()
            {
                Permissions = SharedAccessPermissions.Write,
                SharedAccessExpiryTime =
                    DateTime.UtcNow.AddMinutes(60)
            });
            return JsonSerialize(BlobContainer.Uri.AbsoluteUri + "/" + Guid.NewGuid() + sasWithIdentifier);
        }
    }
}

Он работает, но я не могу использовать его с SWFUpload, так как он использует HTTP POST-глагол, а не HTTP PUT-глагол, который ожидает Azure Blob Storage при создании нового элемента blob. Кто-нибудь знает, как обойти это, не создавая собственный пользовательский компонент Silverlight или Flash, который использует глагол HTTP PUT? Когда вы загружаете файлы, мне нужен индикатор выполнения, поэтому представленная форма, использующая PUT, не является оптимальной.

Для тех, кто интересуется клиентским кодом (который не работает, поскольку SWFUpload использует HTTP POST, а не PUT, как ожидает Azure Blob Storage):

    <div id="header">
        <h1 id="logo"><a href="/">SWFUpload</a></h1>
        <div id="version">v2.2.0</div>
    </div>
    <div id="content">
        <h2>Application Demo (ASP.Net 2.0)</h2>
        <div id="swfu_container" style="margin: 0px 10px;">
            <div>
                <span id="spanButtonPlaceholder"></span>
            </div>
            <div id="divFileProgressContainer" style="height: 75px;"></div>
            <div id="thumbnails"></div>
        </div>
    </div>

 <script type="text/javascript" language="javascript">
        $(document).ready(function () {

            $.ajax({
                url: '/Interfaces/UploadService.svc/GetUploadUrl',
                success: function (result) {
                    var parsedResult = $.parseJSON(result);
                    InitUploadFile(parsedResult);
                }
            });


            function InitUploadFile(uploadUrl) {
                //alert(uploadUrl);
                var swfu = new SWFUpload({
                    // Backend Settings
                    upload_url: uploadUrl,
                    post_params: {
                        "ASPSESSID": "<%=Session.SessionID %>"
                    },

                    // File Upload Settings
                    file_size_limit: "100 MB",
                    file_types: "*.*",
                    file_types_description: "All file types",
                    file_upload_limit: "0",    // Zero means unlimited

                    // Event Handler Settings - these functions as defined in Handlers.js
                    //  The handlers are not part of SWFUpload but are part of my website and control how
                    //  my website reacts to the SWFUpload events.
                    file_queue_error_handler: fileQueueError,
                    file_dialog_complete_handler: fileDialogComplete,
                    upload_progress_handler: uploadProgress,
                    upload_error_handler: uploadError,
                    upload_success_handler: uploadSuccess,
                    upload_complete_handler: uploadComplete,

                    // Button settings
                    button_image_url: "Images/swfupload/XPButtonNoText_160x22.png",
                    button_placeholder_id: "spanButtonPlaceholder",
                    button_width: 160,
                    button_height: 22,
                    button_text: '<span class="button">Select files <span class="buttonSmall">(2 MB Max)</span></span>',
                    button_text_style: '.button { font-family: Helvetica, Arial, sans-serif; font-size: 14pt; } .buttonSmall { font-size: 10pt; }',
                    button_text_top_padding: 1,
                    button_text_left_padding: 5,

                    // Flash Settings
                    flash_url: "Js/swfupload-2.2.0/swfupload.swf", // Relative to this file

                    custom_settings: {
                        upload_target: "divFileProgressContainer"
                    },

                    // Debug Settings
                    debug: false
                });
            }
       });
    </script>

Обновление 19. Июнь 21:07:

Я понял, что, поскольку SWFUpload является открытым исходным кодом, я загружаю источник и изменяю глагол с POST на PUT, к сожалению, Flash Player URLRequestMethod не поддерживает другие глаголы, чем GET и POST. Я нашел предполагаемую работу

private function BuildRequest():URLRequest {
   // Create the request object
   var request:URLRequest = new URLRequest();
   request.method = URLRequestMethod.POST;
   request.requestHeaders.push(new URLRequestHeader("X-HTTP-Method-Override", "PUT"));

но это работает только в Adobe Air, а не с Flash Player.

Я читал, что SilverLight 3 и более поздние версии поддерживают HTTP PUT-глагол, поэтому я думаю, что мне нужно написать код SilverLight, чтобы получить здесь свой путь. Я нашел эту статью в блоге, которая, вероятно, поможет мне здесь http://blog.smarx.com/posts/uploading-windows-azure-blobs-from-silverlight-part-1-shared-access-signatures.

Обновление @27. Июнь '11:

Теперь мне удалось загрузить большие файлы (протестированные с файлами 4,5 Гб) с веб-страницы, используя пользовательский клиент Silverlight, который я написал на основе проекта в http://blog.smarx.com/posts/uploading-windows-azure-blobs-from-silverlight-part-1-shared-access-signatures. Поскольку Silverlight поддерживает как HTTP PUT-глагол, требуемый для хранения Azure Blob, так и поддерживает прогрессивные загрузки, теперь у меня есть возможность загружать массивные файлы непосредственно в хранилище Azure Blob, и мне не нужно идти в ASP.NET-решение, я также получить некоторые хорошие индикаторы выполнения, и пользователь может отменить посередине загрузки, если захочет. Использование памяти на сервере минимально, поскольку весь файл не загружается до его размещения в хранилище Azure Blob. Я использую подпись общего доступа (см. Msdn.microsoft.com/en-us/library/ee395415.aspx), которая предоставляется по запросу службы WCF RESTfull по запросу. Я думаю, что это решение - лучшее, что мы нашли. Спасибо.

Обновление @18. Июль '11:

Я создал проект с открытым исходным кодом с тем, что я нашел здесь:

http://azureslfileuploader.codeplex.com/

4b9b3361

Ответ 1

В последнее время я сделал то же самое. Я создал приложение Silverlight Client для обработки измельчения данных и отправки его в Azure.

Это - рабочий пример, который я выполнил, что делает именно это. В значительной степени следуйте этому, и вы работаете почти для вас.

Ответ 2

независимо от того, какой шаблон кода вы используете. Если вы напишете код на стороне сервера, тогда файл перейдет на ваш веб-ресурс, а затем появятся несколько таких проблем, как повторная загрузка роли и повторная попытка неудачных выгрузок. Я удалил эти проблемы с помощью элемента управления Silverlight на стороне клиента, который не только выполнял отказоустойчивые загрузки, но и делал это с большой скоростью. Вы можете загрузить мой образец и прочитать, как я его построил: Выберите элемент управления загрузкой Azure: Silverlight и TPL или HTML5 и AJAX

Ответ 3

Мы можем загружать очень большие файлы в лазурное хранилище, используя параллельную загрузку. Это означает, что нам нужно разделить большие файлы на куски небольших пакетов файлов и и uoploaded эти пакеты. После завершения загрузки мы можем присоединить пакеты к оригинал один. Для полного кода, пожалуйста, обратитесь к следующей ссылке http://tuvian.wordpress.com/2011/06/28/how-to-upload-large-size-fileblob-to-azure-storage-using-asp-netc/

Ответ 4

Для этой части вопроса:

appcmd set config "My Site/MyApp" -section:requestFiltering -requestLimits.maxAllowedContentLength:104857600 -commitpath:apphost 

на сервере, чтобы выйти за пределы этого предела 30 МБ. Но как я могу запустить это на своих серверах Azure?

Вы можете сделать это с помощью задач запуска - см. http://richardprodger.wordpress.com/2011/03/22/azure-iis7-configuration-with-appcmd/