如何从网页上将巨大的文件上传到Azure blob

时间:2011-06-19 12:07:48

标签: asp.net azure azure-storage-blobs

内部ASP.NET有2 GB的寻址空间,但实际上只有不到1 GB的上传空间(参见http://support.microsoft.com/?id=295626)。此外,IIS 7的上限为30 MB(请参阅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 MB的限制。但是如何在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 GB的blob
  2. 如何将网页上传文件作为不占用所有内存的流
  3. 如果解决方案是编写我自己的HttpModule或HttpHandler来处理我的上传,我怎样才能在Azure服务器上安装它?我可以在Azure上使用像http://neatupload.codeplex.com/这样的HttpHandler吗?
  4. 这个项目不在SharePoint上,但我知道在SharePoint中你有一个名为Blob Provider的东西,并且你可以编写自己的东西,是否有Blob Providers for ASP.NET?
  5. 我还可以提一下,上面的代码默认工作正常,文件小于30 MB,我在客户端使用SWFUpload V2.2.0。

    更新19. 6月19日:09: Twitter上的@YvesGoeleven给了我一个使用共享访问签名(请参阅msdn.microsoft.com/en-us/library/ee395415.aspx)并将文件直接上传到Azure Blob存储的提示,而无需通过ASP.NET 。我创建了一个JSON WCF,它向我的blob存储区返回一个有效的SAS ut。

    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动词而不是Azure Blob存储在创建新blob项时所期望的HTTP PUT动词。任何人都知道如何避免使用我自己的自定义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. 6月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

    更新@ 2011年6月27日:

    我现在已成功设法使用我根据http://blog.smarx.com/posts/uploading-windows-azure-blobs-from-silverlight-part-1-shared-access-signatures中的项目编写的自定义Silverlight客户端从网页上传大文件(使用4,5 Gb文件测试)。由于Silverlight支持Azure Blob存储所需的HTTP PUT动词并支持渐进式上传,因此我现在可以将大量文件直接上传到Azure Blob存储,而且我也不必使用ASP.NET解决方案,我也是得到一些不错的进度条,如果他/她想要,用户可以在上传过程中取消。服务器上的内存使用量很小,因为整个文件在放入Azure Blob存储之前未上载。我使用共享访问签名(请参阅msdn.microsoft.com/en-us/library/ee395415.aspx),该签名是根据请求从WCF RESTfull服务提供的。我认为这个解决方案是我们找到的最好的解决方案。感谢。

    更新@ 18. July '11:

    我用我在这里找到的东西创建了一个开源项目:

    http://azureslfileuploader.codeplex.com/

4 个答案:

答案 0 :(得分:3)

我最近做了同样的事情。我创建了一个Silverlight客户端应用程序来处理切碎数据并将其发送到Azure。

This是我遵循的一个工作示例。几乎遵循这一点,你的工作几乎为你做了很多。

答案 1 :(得分:3)

无论您使用何种代码模式。如果您编写服务器端代码,那么文件将转到您的webrole,然后会出现一些痛苦,例如角色回收和重试失败的上传。我通过客户端Silverlight控件删除了这些问题,这不仅能够容错上传,而且能够以极快的速度完成。您可以下载我的样本并阅读我如何构建它:Pick Your Azure File Upload Control: Silverlight and TPL or HTML5 and AJAX

答案 2 :(得分:2)

我们可以使用并行上传将非常大的文件上传到azure存储中。 这意味着我们需要将大文件拆分成小块文件包 并且加载了这些数据包。上传完成后,我们可以加入数据包 原来的。 有关完整代码,请参阅以下链接 http://tuvian.wordpress.com/2011/06/28/how-to-upload-large-size-fileblob-to-azure-storage-using-asp-netc/

答案 3 :(得分:-1)

对于这部分问题:

appcmd set config "My Site/MyApp" -section:requestFiltering -requestLimits.maxAllowedContentLength:104857600 -commitpath:apphost 
     服务器上的

超出此30 MB的限制。但是如何在Azure服务器上运行它?

您可以使用启动任务执行此操作 - 请参阅http://richardprodger.wordpress.com/2011/03/22/azure-iis7-configuration-with-appcmd/