在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
}
}
这里我们可以看到,使用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("上传格式错误");
}
}
这里我们可以看到文件的文件名被改写成我们想要的名字了,到这里,出现两个疑问。
1.为什么重写GetLocalFileName方法就可以重写文件名呢?
2.文件是怎么保存的呢?
我们先来讲解第二个问题,因为第二个问题讲解完了 第一个问题就知道答案了
文件是怎么保存的呢?
await Request.Content.ReadAsMultipartAsync(provider);
文件能够自动保存的秘密就在这个ReadAsMultipartAsync这个方法里面,下面我们来看看它的源代码是怎么样的。
1.首先ReadAsMultipartAsync中获取了请求中的流,然后存储在MultipartAsyncContext里面,然后把MultipartAsyncContext作为参数调用了MultipartReadAsync方法。
2.在MultipartReadAsync方法中,这里我也不知道这个方法做了什么,但是我们看到了WriteSegment方法,看起来比较可疑,看看这个方法是什么来的。
3.在WriteSegment方法中,我们发现调用了GetOutputStream()方法 ,细心的小伙伴可能会发现,GetOutputStream调用了MultipartFormDataStreamProvider的GetStream方法
4.GetStream方法中,我们可以通过源代码发现,这个方法的目的是为了获取被File.Create创造的文件的流,而想要流,就要有文件的路径,这里它调用了GetLocalFileName方法,这里就解答了为什么重写GetLocalFileName方法会改写文件名。
WriteSegment方法获取了文件的流之后,使用WriteAsync方法向流里面写东西进去,写入的东西我猜是上传文件的数据。
下面我们来简单回答下我们提出的两个问题:
2.文件是怎么保存的呢?
调用ReadAsMultipartAsync的过程中,它首先根据文件路径 (MultipartFormDataStreamProvider提供) 创造一个文件并获取这个文件的流,然后在把数据写进流里面保存,这样,一个文件就保存完成了。
1.为什么重写GetLocalFileName方法就可以重写文件名呢?
因为在保存文件的过程中需要获取文件的流,而获取文件的流需要文件的路径,文件的路径是由MultipartFormDataStreamProvider的GetStream和GetLocalFileName
到这里,文件的保存过程就讲完了,以上是我的观点,如果有什么说错的地方,别喷O(∩_∩)O,欢迎留言。