【问题标题】:Upload files to Azure Blob storage through WebApi without accessing local filesystem通过 WebApi 将文件上传到 Azure Blob 存储,无需访问本地文件系统
【发布时间】:2013-11-03 05:09:52
【问题描述】:

我正在尝试通过 web-api 将文件从本地命令行客户端上传到 Azure 存储。 为此,我正在使用 Azure 网站。与客户合作不是问题。而且我在本地一切正常。这是web-api代码:

    public async Task<HttpResponseMessage> PostUpload()
    {
        // need a local resource to store uploaded files temporarily
        LocalResource localResource = null;
        try
        {
            // Azure web-site fails here
            localResource = RoleEnvironment.GetLocalResource("TempStorage");
        }
        catch (Exception e)
        {
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Unable to get access to local resources");
        }

        var provider = new MultipartFormDataStreamProvider(localResource.RootPath);

        // Read the form data.
        await Request.Content.ReadAsMultipartAsync(provider);

        // snipped validation code
        var container = // code to get container

        foreach (var fileData in provider.FileData)
        {
            var filename = GetBlobName(fileData);
            var blob = container.GetBlockBlobReference(filename);
            using (var filestream = File.OpenRead(fileData.LocalFileName))
            {
                blob.UploadFromStream(filestream);
            }
            File.Delete(fileData.LocalFileName);
        }

        return Request.CreateResponse(HttpStatusCode.OK);
    }

当我在本地运行时一切正常,但是一旦我在 Azure 中部署网站,我就无法上传,因为 Azure 网站无权访问 LocalResource。我需要切换到 Azure Web-Role。我可以切换,但访问本地文件系统让我很困扰。

MultipartFormDataStreamProvider() 的实例需要 LocalResource。而且我还没有找到将文件上传到 WebApi 的替代方法。我的计划是channel through upload directly to Azure,而不在本地硬盘上存储任何内容。

还有其他上传文件的方法吗?

附言我见过usages of Shared Access Signatures,在那里我可以为客户端应用程序提供一个带有签名的 url,并让客户端直接上传到 Azure 博客。但是我不确定这将是多么安全,并且(还)不太舒服将签名传递给客户端。目前我认为客户端将在非常恶劣的环境中运行,并且从客户端返回的任何内容都不可信。

UPD 我的最终解决方案涉及使用在服务器上发布并传递给客户端的只写共享访问签名。然后客户端将文件直接上传到 Azure。这样我就省去了管理上传文件的很多麻烦。这是我的解决方案的more detailed description

【问题讨论】:

  • 以下代码“localResource = RoleEnvironment.GetLocalResource("TempStorage")”抛出了什么异常
  • 您对使用 REST API 感到满意吗?
  • @BrianDishaw 该异常类似于“无法获取 LocalResource,System.Runtime.InteropServices.SEHException (0x80004005):外部组件已引发异常。”。此外,此页面 (msdn.microsoft.com/en-us/library/windowsazure/ee758708.aspx) 建议本地资源仅适用于 Web 和 Worker 角色,而不适用于网站。
  • @VJD 是的,有点。估计得硬着头皮,生成共享访问签名传给客户端,然后客户端通过签名直接上传文件到Azure。
  • 你可能会看看我在这篇文章中的回答:stackoverflow.com/questions/15842496/… ...这里我没有具体谈论上传到 Azure,但它可能会给你一个好主意。

标签: c# azure asp.net-web-api azure-blob-storage


【解决方案1】:

这并不完全是您正在寻找的答案,但您可以使用 MultiPartFileStreamProvider 和 Path.GetTempPath() 将本地存储与 Azure 网站一起使用。代码看起来像这样

public async Task<HttpResponseMessage> PostUpload()
{
    var provider = new MultipartFileStreamProvider(Path.GetTempPath());

    // Read the form data.
    await Request.Content.ReadAsMultipartAsync(provider);

    // do the rest the same     
}

【讨论】:

  • 现在我想知道 Azure 如何管理网站中的 TempPath() 以及它的可靠性如何。大文件上传中途不能擦吗?
  • 我已经删除了表明临时目录不是临时目录的死链接。据我所知,这仍然是正确的。
【解决方案2】:

我发现这篇 StackOverflow 文章覆盖了 MultipartFormDataStreamProvider,因此文件不会首先存储在本地,而是直接写入 AWSStream。看: Is it possible to override MultipartFormDataStreamProvider so that is doesn't save uploads to the file system?

但我不得不说我也喜欢trailmax的解决方案。

【讨论】:

    【解决方案3】:

    让您的代码与 LocalResource 一起工作的一种可能解决方案是将其托管在一个工作进程中,该工作进程通过 Owin 自托管 Web api。

    您可以在以下位置找到简单的演练:http://www.asp.net/web-api/overview/hosting-aspnet-web-api/host-aspnet-web-api-in-an-azure-worker-role

    您只需要在 RoleEntryPoint 的 OnStart() 方法中启动 Owin 托管的 api。请记住,您还可以从 Web api 响应返回 Html,这样您就可以使工作人员角色成为一个非常灵活的基础项目。

    这是一个快速的 sn-p,显示如何通过上面的链接设置 Owin 主机:

    private IDisposable _webApp = null;
    
    public override bool OnStart() {
       ServicePointManager.DefaultConnectionLimit = 5;
    
       var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["DefaultEndpoint"];
       var baseUri = string.Format("{0}://{1}", endpoint.Protocol, endpoint.IPEndpoint);
    
       _webApp = WebApp.Start<Startup>(new StartOptions(baseUri));
    
       return base.OnStart();
    }
    
    public override void OnStop() {
        if (_webApp != null) {
            _webApp.Dispose();
        }
        base.OnStop();
    }
    
    ...
    
    using System.Web.Http;
    using Owin;
    
    class Startup {
        public void Configuration(IAppBuilder app) {
            var config = new HttpConfiguration();
    
            config.Routes.MapHttpRoutes("Default", "{controller}/{id}",
                                        new { id = RouteParameter.Optional });
    
            app.UseWebApi(config);
    
        }
    }
    

    【讨论】:

    • 我本可以将该站点部署到一个网络角色中并拥有一个可用的 LocalResource。仅将 web-api 部署到工作角色中是一种矫枉过正的做法。无论如何,我通过在 blob 存储中发布只写共享访问签名解决了这个问题,并通过 WebApi 将其传递给客户端。然后客户端将文件直接上传到 Azure。
    猜你喜欢
    • 2012-11-04
    • 2022-01-12
    • 2012-03-22
    • 2017-01-24
    • 2017-08-19
    • 1970-01-01
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    相关资源
    最近更新 更多