加入收藏 | 设为首页 | 会员中心 | 我要投稿 济源站长网 (https://www.0391zz.cn/)- 数据工具、数据仓库、行业智能、CDN、运营!
当前位置: 首页 > 站长学院 > Asp教程 > 正文

浅谈ASP.NET Core静态文件处理源码探究

发布时间:2020-08-22 03:15:04 所属栏目:Asp教程 来源:网络整理
导读:这篇文章主要介绍了浅谈ASP.NET Core静态文件处理源码探究,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随

关于FileExtensionContentTypeProvider这里就不作讲解了,主要是承载文件扩展名和MimeType的映射关系代码不复杂,但是映射关系比较多,有兴趣的可以自行查看FileExtensionContentTypeProvider源码,通过上面我们可以看到,最终执行文件相关操作的是StaticFileContext类[点击查看StaticFileContext源码]

internal struct StaticFileContext { private const int StreamCopyBufferSize = 64 * 1024; private readonly HttpContext _context; private readonly StaticFileOptions _options; private readonly HttpRequest _request; private readonly HttpResponse _response; private readonly ILogger _logger; private readonly IFileProvider _fileProvider; private readonly string _method; private readonly string _contentType; private IFileInfo _fileInfo; private EntityTagHeaderValue _etag; private RequestHeaders _requestHeaders; private ResponseHeaders _responseHeaders; private RangeItemHeaderValue _range; private long _length; private readonly PathString _subPath; private DateTimeOffset _lastModified; private PreconditionState _ifMatchState; private PreconditionState _ifNoneMatchState; private PreconditionState _ifModifiedSinceState; private PreconditionState _ifUnmodifiedSinceState; private RequestType _requestType; public StaticFileContext(HttpContext context, StaticFileOptions options, ILogger logger, IFileProvider fileProvider, string contentType, PathString subPath) { _context = context; _options = options; _request = context.Request; _response = context.Response; _logger = logger; _fileProvider = fileProvider; _method = _request.Method; _contentType = contentType; _fileInfo = null; _etag = null; _requestHeaders = null; _responseHeaders = null; _range = null; _length = 0; _subPath = subPath; _lastModified = new DateTimeOffset(); _ifMatchState = PreconditionState.Unspecified; _ifNoneMatchState = PreconditionState.Unspecified; _ifModifiedSinceState = PreconditionState.Unspecified; _ifUnmodifiedSinceState = PreconditionState.Unspecified; //再次判断请求HttpMethod if (HttpMethods.IsGet(_method)) { _requestType = RequestType.IsGet; } else if (HttpMethods.IsHead(_method)) { _requestType = RequestType.IsHead; } else { _requestType = RequestType.Unspecified; } } /// <summary> /// 判断文件是否存在 /// </summary> public bool LookupFileInfo() { //判断根据请求路径是否可以获取到文件信息 _fileInfo = _fileProvider.GetFileInfo(_subPath.Value); if (_fileInfo.Exists) { //获取文件长度 _length = _fileInfo.Length; //最后修改日期 DateTimeOffset last = _fileInfo.LastModified; _lastModified = new DateTimeOffset(last.Year, last.Month, last.Day, last.Hour, last.Minute, last.Second, last.Offset).ToUniversalTime(); //ETag标识 long etagHash = _lastModified.ToFileTime() ^ _length; _etag = new EntityTagHeaderValue('"' + Convert.ToString(etagHash, 16) + '"'); } return _fileInfo.Exists; } /// <summary> /// 处理文件输出 /// </summary> public async Task ServeStaticFile(HttpContext context, RequestDelegate next) { //1.准备输出相关Header,主要是获取和输出静态文件输出缓存相关的内容 //2.我们之前提到的OnPrepareResponse也是在这里执行的 ComprehendRequestHeaders(); //根据ComprehendRequestHeaders方法获取到的文件状态进行判断 switch (GetPreconditionState()) { case PreconditionState.Unspecified: //处理文件输出 case PreconditionState.ShouldProcess: //判断是否是Head请求 if (IsHeadMethod) { await SendStatusAsync(Constants.Status200Ok); return; } try { //判断是否包含range请求,即文件分段下载的情况 if (IsRangeRequest) { await SendRangeAsync(); return; } //正常文件输出处理 await SendAsync(); _logger.FileServed(SubPath, PhysicalPath); return; } catch (FileNotFoundException) { context.Response.Clear(); } await next(context); return; case PreconditionState.NotModified: await SendStatusAsync(Constants.Status304NotModified); return; case PreconditionState.PreconditionFailed: await SendStatusAsync(Constants.Status412PreconditionFailed); return; default: var exception = new NotImplementedException(GetPreconditionState().ToString()); throw exception; } } /// <summary> /// 通用文件文件返回处理 /// </summary> public async Task SendAsync() { SetCompressionMode(); ApplyResponseHeaders(Constants.Status200Ok); string physicalPath = _fileInfo.PhysicalPath; var sendFile = _context.Features.Get<IHttpResponseBodyFeature>(); //判断是否设置过输出特征操作相关,比如是否启动输出压缩,或者自定义的输出处理比如输出加密等等 if (sendFile != null && !string.IsNullOrEmpty(physicalPath)) { await sendFile.SendFileAsync(physicalPath, 0, _length, CancellationToken.None); return; } try { //不存在任何特殊处理的操作作,直接读取文件返回 using (var readStream = _fileInfo.CreateReadStream()) { await StreamCopyOperation.CopyToAsync(readStream, _response.Body, _length, StreamCopyBufferSize, _context.RequestAborted); } } catch (OperationCanceledException ex) { _context.Abort(); } } /// <summary> /// 分段请求下载操作处理 /// </summary> internal async Task SendRangeAsync() { if (_range == null) { ResponseHeaders.ContentRange = new ContentRangeHeaderValue(_length); ApplyResponseHeaders(Constants.Status416RangeNotSatisfiable); _logger.RangeNotSatisfiable(SubPath); return; } //计算range相关header数据 ResponseHeaders.ContentRange = ComputeContentRange(_range, out var start, out var length); _response.ContentLength = length; //设置输出压缩相关header SetCompressionMode(); ApplyResponseHeaders(Constants.Status206PartialContent); string physicalPath = _fileInfo.PhysicalPath; var sendFile = _context.Features.Get<IHttpResponseBodyFeature>(); //判断是否设置过输出特征操作相关,比如是否启动输出压缩,或者自定义的输出处理比如输出加密等等 if (sendFile != null && !string.IsNullOrEmpty(physicalPath)) { _logger.SendingFileRange(_response.Headers[HeaderNames.ContentRange], physicalPath); await sendFile.SendFileAsync(physicalPath, start, length, CancellationToken.None); return; } try { using (var readStream = _fileInfo.CreateReadStream()) { readStream.Seek(start, SeekOrigin.Begin); _logger.CopyingFileRange(_response.Headers[HeaderNames.ContentRange], SubPath); //设置文件输出起始位置和读取长度 await StreamCopyOperation.CopyToAsync(readStream, _response.Body, length, _context.RequestAborted); } } catch (OperationCanceledException ex) { _context.Abort(); } } }

(编辑:济源站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读