IApplicationBuilderExtensions_UseStaticFiles.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using Microsoft.AspNetCore.Builder;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.StaticFiles;
  4. using Microsoft.Extensions.FileProviders;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Threading.Tasks;
  10. using Vit.Core.Util.Common;
  11. using Vit.WebHost;
  12. namespace Vit.Extensions
  13. {
  14. public static class IApplicationBuilderExtensions_UseStaticFiles
  15. {
  16. /// <summary>
  17. /// 启用静态文件服务
  18. ///
  19. /// <example>
  20. /// <code>
  21. /// /* 映射静态文件。若不指定则不映射静态文件 */
  22. /// "staticFiles": {
  23. ///
  24. /// /* 请求路径(可不指定)。demo:"/file/static"。The relative request path that maps to static resources */
  25. /// //"requestPath": "/file",
  26. ///
  27. /// /* 静态文件路径。可为相对路径或绝对路径。若为空或空字符串则为默认路径(wwwroot)。demo:"wwwroot/demo" */
  28. /// //"rootPath": "wwwroot",
  29. ///
  30. /// /* 默认页面(可不指定)。An ordered list of file names to select by default. List length and ordering may affect performance */
  31. /// //"defaultFileNames": [],
  32. ///
  33. /// /* 是否可浏览目录(default false)。Enables directory browsing */
  34. /// //"useDirectoryBrowser": false,
  35. ///
  36. /// /* 静态文件类型映射配置的文件路径。可为相对路径或绝对路径。例如"contentTypeMap.json"。若不指定(或指定的文件不存在)则不指定文件类型映射配置 */
  37. /// "contentTypeMapFile": "contentTypeMap.json",
  38. ///
  39. /// /* 回应静态文件时额外添加的http回应头。可不指定。 */
  40. /// "responseHeaders": {
  41. ///
  42. /// //设置浏览器静态文件缓存3600秒
  43. /// "Cache-Control": "public,max-age=3600"
  44. /// }
  45. /// }
  46. ///
  47. /// </code>
  48. /// </example>
  49. ///
  50. /// </summary>
  51. /// <param name="data"></param>
  52. /// <param name="configPath">在appsettings.json文件中的路径。默认:"server.staticFiles"。</param>
  53. /// <returns></returns>
  54. public static IApplicationBuilder UseStaticFilesFromConfig(this IApplicationBuilder data, string configPath = "server.staticFiles")
  55. {
  56. return data.UseStaticFiles(Vit.Core.Util.ConfigurationManager.ConfigurationManager.Instance.GetByPath<Vit.WebHost.StaticFilesConfig>(configPath));
  57. }
  58. /// <summary>
  59. /// 启用静态文件服务
  60. ///
  61. /// <example>
  62. /// <code>
  63. /// /* 映射静态文件。若不指定则不映射静态文件 */
  64. /// "staticFiles": {
  65. ///
  66. /// /* 请求路径(可不指定)。demo:"/file/static"。The relative request path that maps to static resources */
  67. /// //"requestPath": "/file",
  68. ///
  69. /// /* 静态文件路径。可为相对路径或绝对路径。若为空或空字符串则为默认路径(wwwroot)。demo:"wwwroot/demo" */
  70. /// //"rootPath": "wwwroot",
  71. ///
  72. /// /* 默认页面(可不指定)。An ordered list of file names to select by default. List length and ordering may affect performance */
  73. /// //"defaultFileNames": [],
  74. ///
  75. /// /* 是否可浏览目录(default false)。Enables directory browsing */
  76. /// //"useDirectoryBrowser": false,
  77. ///
  78. /// /* 静态文件类型映射配置的文件路径。可为相对路径或绝对路径。例如"contentTypeMap.json"。若不指定(或指定的文件不存在)则不指定文件类型映射配置 */
  79. /// "contentTypeMapFile": "contentTypeMap.json",
  80. ///
  81. /// /* 回应静态文件时额外添加的http回应头。可不指定。 */
  82. /// "responseHeaders": {
  83. ///
  84. /// //设置浏览器静态文件缓存3600秒
  85. /// "Cache-Control": "public,max-age=3600"
  86. /// }
  87. /// }
  88. ///
  89. /// </code>
  90. /// </example>
  91. ///
  92. /// </summary>
  93. /// <param name="data"></param>
  94. /// <param name="config"></param>
  95. /// <returns></returns>
  96. public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder data, StaticFilesConfig config)
  97. {
  98. if (config == null || data == null) return data;
  99. #region (x.1)UseStaticFiles
  100. var staticfileOptions = new StaticFileOptions();
  101. //(x.x.1)requestPath
  102. if (!string.IsNullOrEmpty(config.requestPath))
  103. {
  104. staticfileOptions.RequestPath = new Microsoft.AspNetCore.Http.PathString(config.requestPath);
  105. }
  106. #region (x.x.2)FileProvider
  107. IFileProvider fileProvider=null;
  108. string rootPath;
  109. if (!string.IsNullOrWhiteSpace(rootPath=config.rootPath) && Directory.Exists(rootPath))
  110. {
  111. fileProvider = new PhysicalFileProvider(rootPath);
  112. }
  113. else if (Directory.Exists(rootPath = CommonHelp.GetAbsPath("wwwroot")))
  114. {
  115. fileProvider = new PhysicalFileProvider(rootPath);
  116. }
  117. else
  118. {
  119. var dir = new DirectoryInfo("wwwroot");
  120. if(dir.Exists)
  121. fileProvider = new PhysicalFileProvider(dir.FullName);
  122. }
  123. staticfileOptions.FileProvider = fileProvider;
  124. #endregion
  125. //(x.x.3)OnInitStaticFileOptions
  126. config.OnInitStaticFileOptions?.Invoke(staticfileOptions);
  127. #region (x.x.4)Response Headers
  128. if (config.responseHeaders != null)
  129. {
  130. staticfileOptions.OnPrepareResponse +=
  131. ctx =>
  132. {
  133. var Headers = ctx.Context.Response.Headers;
  134. foreach (var kv in config.responseHeaders)
  135. {
  136. Headers[kv.Key] = kv.Value;
  137. }
  138. };
  139. }
  140. #endregion
  141. //(x.x.5)contentTypeProvider
  142. if (config.contentTypeProvider != null)
  143. {
  144. staticfileOptions.ContentTypeProvider = config.contentTypeProvider;
  145. }
  146. //(x.x.6)UseDefaultFiles
  147. if (config.defaultFileNames != null)
  148. {
  149. data.UseDefaultFiles(new DefaultFilesOptions
  150. {
  151. DefaultFileNames = config.defaultFileNames,
  152. FileProvider= fileProvider
  153. });
  154. }
  155. //(x.x.7)UseStaticFiles
  156. data.UseStaticFiles(staticfileOptions);
  157. #endregion
  158. //(x.2)UseDirectoryBrowser
  159. if (config.useDirectoryBrowser == true)
  160. {
  161. var options = new DirectoryBrowserOptions {
  162. RequestPath = staticfileOptions.RequestPath,
  163. FileProvider = fileProvider,
  164. Formatter = new SortedHtmlDirectoryFormatter()
  165. };
  166. data.UseDirectoryBrowser(options);
  167. }
  168. return data;
  169. }
  170. #region SortedHtmlDirectoryFormatter
  171. /// <summary>
  172. /// 自定义DirectoryFormatter
  173. /// https://www.cnblogs.com/artech/p/static-file-for-asp-net-core-04.html
  174. ///
  175. /// </summary>
  176. public class SortedHtmlDirectoryFormatter : HtmlDirectoryFormatter
  177. {
  178. /// <summary>
  179. /// 默认按文件名称排序。 contents => contents.OrderBy(f => f.Name)
  180. /// </summary>
  181. public Func<IEnumerable<IFileInfo>, IEnumerable<IFileInfo>> SetOrder = contents => contents.OrderBy(f => f.Name);
  182. public SortedHtmlDirectoryFormatter(System.Text.Encodings.Web.HtmlEncoder encoder = null) : base(encoder ?? System.Text.Encodings.Web.HtmlEncoder.Default)
  183. {
  184. }
  185. public override async Task GenerateContentAsync(Microsoft.AspNetCore.Http.HttpContext context, IEnumerable<IFileInfo> contents)
  186. {
  187. contents = SetOrder(contents);
  188. await base.GenerateContentAsync(context, contents);
  189. //context.Response.ContentType = "text/html";
  190. //await context.Response.WriteAsync("<html><head><title>Index</title><body><ul>");
  191. //foreach (var file in contents)
  192. //{
  193. // string href = $"{context.Request.Path.Value.TrimEnd('/')}/{file.Name}";
  194. // await context.Response.WriteAsync($"<li><a href='{href}'>{file.Name}</a></li>");
  195. //}
  196. //await context.Response.WriteAsync("</ul></body></html>");
  197. }
  198. }
  199. #endregion
  200. }
  201. }