ASP.NET Web API 文件保存为 "BodyPart_3ded2bfb-40be-4183-b789-9301f93e90af"

35
我使用ASP.NET Web API上传文件。在RC之前我已经这样做过,但出现了问题,文件名保存为"BodyPart_3ded2bfb-40be-4183-b789-9301f93e90af"而不是文件名。下面的文件名变量也返回了这个字符串而不是文件名。我似乎无法弄清楚我的错误所在。希望得到任何帮助。
function upload() {
    $("#divResult").html("Uploading...");
    var formData = new FormData($('form')[0]); 
    $.ajax({
        url: 'api/files/uploadfile?folder=' + $('#ddlFolders').val(),
        type: 'POST',
        success: function (data) {
            $("#divResult").html(data);
        },
        data: formData,
        cache: false,
        contentType: false,
        processData: false
    });
}; 

控制器:

    public Task<HttpResponseMessage> UploadFile([FromUri]string folder)
    {
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.UnsupportedMediaType));
        }

        // Save file
        MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files"));
        Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider);

        return task.ContinueWith<HttpResponseMessage>(contents =>
        {
            string filename = provider.BodyPartFileNames.First().Value;
            return new HttpResponseMessage()
          {
              Content = new StringContent(string.Format("File saved in {0}.", folder))
          };

        }, TaskScheduler.FromCurrentSynchronizationContext());
文件看起来像这样:

enter image description here


我在这里写了完整的示例:http://rivdiv.wordpress.com/2012/06/13/uploading-a-file-using-asp-net-web-api/ - Rivka
3个回答

48

这是我们有意做出的变更——考虑到使用Content-Disposition头字段提供的文件名存在安全风险,现在我们会计算一个文件名并将其显示出来。

如果您希望自己控制服务器本地文件名,那么可以从MultipartFormDataStreamProvider派生并重写GetLocalFileName以提供所需的任何名称。不过请注意,这样做可能存在安全考虑。

希望对您有所帮助,

Henrik


3
谢谢您的回复。那么我应该如何正确保存实际文件呢?现在它是“文件”数据类型,没有任何扩展名,也无法打开。 - Rivka
5
我找到了一个现时的解决方法 - 使用File.Move,并通过使用provider.BodyPartFileNames.FirstOrDefault().Key.TrimStart('"').TrimEnd('"') 来获取原始文件名并添加正确的扩展名。虽然这感觉有点反向,但我会很感激任何启示。谢谢。 - Rivka
2
公共类FormDataStreamer:MultipartFormDataStreamProvider { public FormDataStreamer(string rootPath) : base(rootPath) { }public FormDataStreamer(string rootPath, int bufferSize) : base(rootPath, bufferSize) { } public override string GetLocalFileName(HttpContentHeaders headers) { var srcFileName = headers.ContentDisposition.FileName.Replace("\"", ""); return Guid.NewGuid() + Path.GetExtension(srcFileName); }} - Konstantin Fedoseev
嗨,亨利克,我很好奇,您能详细说明一下使用自定义MultipartFormDataStreamProvider和覆盖GetLocalFileName时的安全考虑吗? - Stig Perez

19

我更新了教程的代码,使其适用于ASP.NET Web API RC。事实上,正如Henrik所提到的那样,Content-Disposition不再用作文件名。请参见本文底部的源文件- http://www.strathweb.com/2012/04/html5-drag-and-drop-asynchronous-multi-file-upload-with-asp-net-webapi/

请注意,MultipartFormDataStreamProvider还有其他更改未被包含在RC中,因此现在它更加灵活。Henrik在这里发布了相关博客文章-http://blogs.msdn.com/b/henrikn/archive/2012/04/27/asp-net-web-api-updates-april-27.aspx

编辑:我已经在博客中介绍了Web API RTM中上传文件的新和改进方法,希望这能帮助组织和理清事物- http://www.strathweb.com/2012/08/a-guide-to-asynchronous-file-uploads-in-asp-net-web-api-rtm/


谢谢,你的例子很容易阅读和理解。我之前用beta代码写了自己的程序,但在RC中尝试实现时却让我抓狂了。 - Keith
谢谢Filip。所以我认为正确的方法是从密钥获取原始文件,然后移动/重命名文件。没有遇到任何Web API文章指定这个特定的RC更改,很高兴我把这个问题提出来了。谢谢。 - Rivka
这真是太棒了。像魔法一样运行! - Detilium
非常感谢您,真的很感激。我不知道这是这种情况。 - Egli Becerra

14

这对我有用

在API控制器中

// We implement MultipartFormDataStreamProvider to override the filename of File which
// will be stored on server, or else the default name will be of the format like Body-
// Part_{GUID}. In the following implementation we simply get the FileName from 
// ContentDisposition Header of the Request Body.
public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
    public CustomMultipartFormDataStreamProvider(string path) : base(path) { }

    public override string GetLocalFileName(HttpContentHeaders headers)
    {
        return headers.ContentDisposition.FileName.Replace("\"", string.Empty);
    }
}

那么

 string root = HttpContext.Current.Server.MapPath("~/App_Data");       
 CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(root);

谢谢,


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接