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

Внутренне 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/, но мне действительно нужно, чтобы они были загружены с веб-страницы.

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

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

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

Обновление 19 июня, 19:09: @YvesGoeleven в Twitter дал мне совет по использованию подписи общего доступа (см. msdn.microsoft.com/en-us/library/ee395415.aspx) и загрузке файла напрямую в хранилище BLOB-объектов Azure, вообще не используя 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, которую хранилище BLOB-объектов Azure ожидает при создании нового элемента BLOB-объектов. Кто-нибудь знает, как обойти это, не создавая собственный клиентский компонент Silverlight или Flash, который использует команду HTTP PUT? Мне нужен индикатор выполнения при загрузке файлов, поэтому отправленная форма, использующая PUT, не оптимальна.

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

    <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-подписи.

Обновление от 27 июня 2011 г .:

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

Обновление от 18 июля 2011 г .:

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

http://azureslfileuploader.codeplex.com/


person Community    schedule 19.06.2011    source источник


Ответы (4)


Я действительно сделал то же самое недавно. Я создал клиентское приложение Silverlight, чтобы обрабатывать разделение данных и их отправку в Azure.

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

person Gabe    schedule 27.06.2011

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

person Rahul    schedule 01.11.2011

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

person tuvian    schedule 28.06.2011

По этой части вопроса:

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/

person Stuart    schedule 20.06.2011