ASP.NET Web api接收multipart/form-data文件,解析MultipartFormDataStreamProvider文件保存过程

在ASP.NET Web api 中,接收multipart/form-data文件,我们可以使用MultipartFormDataStreamProvider来保存图片

   [HttpPost,Route("api/Job/newJob")]
    public async Task PostNewJob()
    {
           string root = HttpContext.Current.Server.MapPath("~/img/workImg");//指定要将文件存入的服务器物理位置
           //继承MultipartFormDataStreamProvider类
           var provider =new MultipartFormDataStreamProvider(root);
           try
           {       //执行完这条之后,文件便保存了
                await Request.Content.ReadAsMultipartAsync(provider);
           }
            catch (IOException innException)
            {     //下面可以不用看,主要是上面的代码
                //检测是否是MyMultipartFormDataStream的 GetLocalFileName方法发生异常
                if (innException.InnerException.InnerException.InnerException == null)
                {
                     return Json(new { code = "500", Message = "文件写入错误" });
                }
                return Json(new { code = "500", Message = innException.InnerException.InnerException.InnerException.Message          
            }
    }

ASP.NET Web api接收multipart/form-data文件,解析MultipartFormDataStreamProvider文件保存过程_第1张图片

这里我们可以看到,使用MultipartFormDataStreamProvider默认保存的文件名的格式是BodyPart加上一串数字字母组合而成,如果我们想要自定义文件名的话,我们可以继承MultipartFormDataStreamProvider类并重写GetLocalFileName方法

 public class MyMultipartFormDataStreamProvider:MultipartFormDataStreamProvider
 {
       public MyMultipartFormDataStreamProvider(string path) : base(path) { }
       
       public override string GetLocalFileName(HttpContentHeaders headers)
       {
            //这里获取上传的文件名
            string Name = headers.ContentDisposition.FileName.Replace("\"", string.Empty);
            
            //这里做了一个判断,只有jpg,png,gif为后缀的,才给保存,否则抛出一个错误(写这个判断的原因是因为需求原因)
             if (Name.EndsWith(".jpg", StringComparison.CurrentCultureIgnoreCase) || 
                 Name.EndsWith(".png", StringComparison.CurrentCultureIgnoreCase) ||
                 Name.EndsWith(".gif", StringComparison.CurrentCultureIgnoreCase))
             {
                   //以ContentDisposition的哈希值加上传的名字作为文件名
                   return $"{headers.ContentDisposition.GetHashCode()}_{Name}";
             }
             throw new InvalidOperationException("上传格式错误");
       }
 }

ASP.NET Web api接收multipart/form-data文件,解析MultipartFormDataStreamProvider文件保存过程_第2张图片
这里我们可以看到文件的文件名被改写成我们想要的名字了,到这里,出现两个疑问。
1.为什么重写GetLocalFileName方法就可以重写文件名呢?

2.文件是怎么保存的呢?
我们先来讲解第二个问题,因为第二个问题讲解完了 第一个问题就知道答案了


文件是怎么保存的呢?

await Request.Content.ReadAsMultipartAsync(provider);

文件能够自动保存的秘密就在这个ReadAsMultipartAsync这个方法里面,下面我们来看看它的源代码是怎么样的。

ASP.NET Web api接收multipart/form-data文件,解析MultipartFormDataStreamProvider文件保存过程_第3张图片
在这里插入图片描述
1.首先ReadAsMultipartAsync中获取了请求中的流,然后存储在MultipartAsyncContext里面,然后把MultipartAsyncContext作为参数调用了MultipartReadAsync方法。

ASP.NET Web api接收multipart/form-data文件,解析MultipartFormDataStreamProvider文件保存过程_第4张图片
2.在MultipartReadAsync方法中,这里我也不知道这个方法做了什么,但是我们看到了WriteSegment方法,看起来比较可疑,看看这个方法是什么来的。

ASP.NET Web api接收multipart/form-data文件,解析MultipartFormDataStreamProvider文件保存过程_第5张图片
3.在WriteSegment方法中,我们发现调用了GetOutputStream()方法 ,细心的小伙伴可能会发现,GetOutputStream调用了MultipartFormDataStreamProvider的GetStream方法

ASP.NET Web api接收multipart/form-data文件,解析MultipartFormDataStreamProvider文件保存过程_第6张图片
4.GetStream方法中,我们可以通过源代码发现,这个方法的目的是为了获取被File.Create创造的文件的流,而想要流,就要有文件的路径,这里它调用了GetLocalFileName方法,这里就解答了为什么重写GetLocalFileName方法会改写文件名。
WriteSegment方法获取了文件的流之后,使用WriteAsync方法向流里面写东西进去,写入的东西我猜是上传文件的数据。

下面我们来简单回答下我们提出的两个问题:
2.文件是怎么保存的呢?
调用ReadAsMultipartAsync的过程中,它首先根据文件路径 (MultipartFormDataStreamProvider提供) 创造一个文件并获取这个文件的流,然后在把数据写进流里面保存,这样,一个文件就保存完成了。

1.为什么重写GetLocalFileName方法就可以重写文件名呢?
因为在保存文件的过程中需要获取文件的流,而获取文件的流需要文件的路径,文件的路径是由MultipartFormDataStreamProvider的GetStream和GetLocalFileName

到这里,文件的保存过程就讲完了,以上是我的观点,如果有什么说错的地方,别喷O(∩_∩)O,欢迎留言。

你可能感兴趣的:(ASP.NET)