• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
IT联盟之家-ithome8
博客园    首页    新随笔    联系   管理    订阅  订阅
.net6文件服务开发实现类似OSS的功能

目前OSS服务有很多,比如阿里云、腾讯等都有提供,当然这些都是要收费的,费用一般是按流量来收费,不贵。但是在前期一般不需要使用OSS来存储和获取静态文件,所以一般文件都是保存到后台服务器。下面我写的文件服务大概实现步骤和原理。

一、文件上传,一般文件是由后台上传,系统需要使用到的文件主要是图片、文档(word,excel,pdf)、视频等,其中图片有缩略图的使用,很多地方要使用图片,后台上传时是不知道会要用到哪些尺寸的缩略图的,所以在上传的时候生成缩略图不现实(早期很多商城系统是在上传的时候就生成了一些固定尺寸的缩略图),我这里采用的是使用时自动生成。文件上传实现还比较简单,后端接口直接获取File读取内容进行保存处理即可,前端使用VUE的话也简单,代码如下:

 前端代码,我这里上传的文件类型,包括图片、文档、视频,type就是文件类型1 图片 2 视频 5 文档。视频和文档需要上传封面图片,并且可以指定授权访问权限,如果非公开访问
的文件需要授权申请才可以访问,在前端上传时我踩到了一个坑,使用VUE上传文件,后端获取不到文件内容,原因是nginx反向代理将文件流的内容过滤掉了,接口获取不到文件流的内
容,我这里采取了另外一个方法上传图片,就是将图片转换为base64编码,通过字符串的参数传到接口,接口对base64编码进行解码,将base64编码解码成字节流,然后保存到服务器
  1 <el-form ref="form" label-width="134px" :model="form" size="small" :rules="rules">
  2         <el-form-item :key="1" label="本地资料:" v-if="type === 5" prop="documentSrc">
  3           <el-upload show-word-limit list-type="picture-card" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx"
  4             :class="{ 'upload-max': form.documentList.length >= 1 }" :file-list="form.documentList"
  5             :on-change="documentChange" action="#" multiple :limit="1" :auto-upload="false">
  6             <i class="el-icon-plus" slot="trigger"></i>
  7             <div slot="file" slot-scope="{file}">
  8               <video :src="file.url" :autoplay="false"
  9                 style="width:148px;height:148px;background:rgba(0,0,0, .85);"></video>
 10               <i class="el-icon-delete video-delete" @click="documentDelete"></i>
 11             </div>
 12             <div slot="tip" class="el-upload__tip">
 13               资料文档大小不超过30M,支持pdf,word,excel,ppt格式
 14             </div>
 15           </el-upload>
 16         </el-form-item>
 17         <el-form-item label="名称:" :key="5" prop="documentName" v-if="type === 5">
 18           <el-col :span="7">
 19             <el-input v-model="form.documentName" v-trim maxlength="20" show-word-limit />
 20           </el-col>
 21         </el-form-item>
 22         <el-form-item :key="1" label="本地视频:" v-if="type === 2" prop="videoSrc">
 23           <el-upload show-word-limit list-type="picture-card" accept="video/mp4,video/3gp"
 24             :class="{ 'upload-max': form.videoList.length >= 1 }" :file-list="form.videoList" :on-change="videoChange"
 25             action="#" multiple :limit="1" :auto-upload="false">
 26             <i class="el-icon-plus" slot="trigger"></i>
 27             <div slot="file" slot-scope="{file}">
 28               <video :src="file.url" :autoplay="false"
 29                 style="width:148px;height:148px;background:rgba(0,0,0, .85);"></video>
 30               <i class="el-icon-delete video-delete" @click="videoDelete"></i>
 31             </div>
 32             <div slot="tip" class="el-upload__tip">
 33               视频大小不超过30M,支持mp4,3gp格式
 34             </div>
 35           </el-upload>
 36         </el-form-item>
 37         <el-form-item label="名称:" :key="2" prop="videoName" v-if="type === 2">
 38           <el-col :span="7">
 39             <el-input v-model="form.videoName" v-trim maxlength="20" show-word-limit />
 40           </el-col>
 41         </el-form-item>
 42         <el-form-item label="分组:">
 43           <el-col :span="7">
 44             <el-select v-model="selCategory.id" class="dialog-select">
 45               <el-option v-for="(item, index) in category" :key="index" :label="item.name" :value="item.id">
 46               </el-option>
 47             </el-select>
 48           </el-col>
 49         </el-form-item>
 50         <el-form-item :key="3" label="选择本地图片: " required v-if="type === 1">
 51           <el-upload ref="imageUpload" list-type="picture-card" :class="{ 'upload-max': imageFileList.length >= 100 }"
 52             accept="image/jpeg,image/gif,image/png,image/bmp,image/webp" :on-remove="imageChange"
 53             :on-exceed="imageExceed" :on-change="imageChange" :action="imageUploadUrl" multiple :limit="100"
 54             :auto-upload="false" :file-list="imageFileList" :data="imageUploadData" :headers="headers"
 55             :before-upload="beforeImageUpload" :on-success="uploadImageSuccess">
 56             <i class="el-icon-plus" slot="trigger"></i>
 57             <div slot="tip" class="el-upload__tip">
 58               仅支持png, jpg, gif, bmp 4种格式,大小不超过3.0MB
 59             </div>
 60           </el-upload>
 61         </el-form-item>
 62         <el-form-item label="访问权限设置:" v-if="type === 2 || type == 5">
 63           <el-col :span="7">
 64             <el-select v-model="form.accessControl" class="dialog-select">
 65               <el-option label="公开访问" value="0"></el-option>
 66               <el-option label="申请授权访问" value="1"></el-option>
 67               <el-option label="不允许访问" value="2"></el-option>
 68             </el-select>
 69           </el-col>
 70         </el-form-item>
 71         <!-- 视频封面专用 -->
 72         <el-form-item label="封面:" :key="4" v-if="type === 2" prop="videoPoster">
 73           <el-upload list-type="picture-card" accept="image/jpeg,image/gif,image/png,image/bmp"
 74             :class="{ 'upload-max': form.videoPosterList.length >= 1 }" :on-remove="videoPosterRemove"
 75             :on-change="videoPosterChange" :action="imageUploadUrl" :limit="1" :auto-upload="false"
 76             :file-list="form.videoPosterList">
 77             <i class="el-icon-plus" slot="trigger"></i>
 78             <div slot="tip" class="el-upload__tip">
 79               建议尺寸:800*800 像素,支持jpg、gif、png3种格式,大小不超过3MB
 80             </div>
 81           </el-upload>
 82         </el-form-item>
 83         <!-- 资料封面图 -->
 84         <el-form-item label="封面:" :key="4" v-if="type === 5" prop="documentPoster">
 85           <el-upload list-type="picture-card" accept="image/jpeg,image/gif,image/png,image/bmp"
 86             :class="{ 'upload-max': form.documentPosterList.length >= 1 }" :on-remove="documentPosterRemove"
 87             :on-change="documentPosterChange" :action="imageUploadUrl" :limit="1" :auto-upload="false"
 88             :file-list="form.documentPosterList">
 89             <i class="el-icon-plus" slot="trigger"></i>
 90             <div slot="tip" class="el-upload__tip">
 91               建议尺寸:800*800 像素,支持jpg、gif、png3种格式,大小不超过3MB
 92             </div>
 93           </el-upload>
 94         </el-form-item>
 95       </el-form>
 96       <template slot="footer">
 97         <el-button size="small" @click="showDialog = false">取 消</el-button>
 98         <el-button size="small" type="primary" @click="handleConfirmUpload" :loading="uploading">确 定</el-button>
 99       </template>
100 // JS代码,上传实现,其中UploadVideo和UploadDocument分别是上传视频和上传文档的接口定义
101   handleConfirmUpload() {
102       this.$refs['form'].validate((valid) => {
103         if (valid) {
104           if (this.type === 2) {
105             if (!this.beforeVideoUpload(this.form.videoList[0])) {
106               return
107             }
108 
109             if (this.form.videoPosterList[0]) {
110               const isLt3M = this.form.videoPosterList[0].size / 1024 / 1024 < 3
111               if (!isLt3M) {
112                 return this.$message('error', '上传的封面大小不能超过 3.0MB!')
113               }
114             }
115 
116             this.uploading = true
117             const formData = new FormData()
118             formData.append('name', this.form.videoName)
119             formData.append('categoryId', this.selCategory.id)
120             formData.append('video', this.form.videoList[0].raw)
121             formData.append('cover', this.form.videoPosterList[0] ? this.form.videoPosterList[0].raw : '')
122             formData.append('accessControl', this.form.accessControl)
123             UploadVideo(formData).then((res) => {
124               this.showDialog = false
125               this.form.videoName = ''
126               this.form.videoList = []
127               this.uploading = false
128               this.form.videoPosterList = []
129               this.loadCategory()
130             })
131           } else if (this.type === 5) {
132             if (!this.beforeDocumentUpload(this.form.documentList[0])) {
133               return
134             }
135 
136             if (this.form.documentPosterList[0]) {
137               const isLt3M = this.form.documentPosterList[0].size / 1024 / 1024 < 3
138               if (!isLt3M) {
139                 return this.$message('error', '上传的封面大小不能超过 3.0MB!')
140               }
141             }
142 
143             this.uploading = true
144             const formData = new FormData()
145             formData.append('name', this.form.documentName)
146             formData.append('categoryId', this.selCategory.id)
147             formData.append('document', this.form.documentList[0].raw)
148             formData.append('cover', this.form.documentPosterList[0] ? this.form.documentPosterList[0].raw : '')
149             formData.append('accessControl', this.form.accessControl)
150             UploadDocument(formData).then((res) => {
151               this.showDialog = false
152               this.form.documentName = ''
153               this.form.documentList = []
154               this.uploading = false
155               this.form.documentPosterList = []
156               this.loadCategory()
157             })
158           } else {
159             if (this.imageFileList.length === 0) {
160               return this.$message('error', '请至少选择一张图片')
161             }
162             this.uploading = true
163             this.$refs.imageUpload.submit()
164           }
165         }
166       })
167     }
View Code

  base64版前端代码,base64版本主要是多了一个步骤,将图片文件转换为base64编码,在el-upload组件里面加上onchange事件,调用getFile方法即可

 1  getFile(file, fileList) {
 2       this.filename = file.name
 3       if (!this.$isImage(this.filename)) {
 4         return this.$message('error', '请选择图片文件')
 5       }
 6       this.list[this.uploadIndex].thumbnails = this.getFilesCode(fileList)
 7       console.log(this.list[this.uploadIndex].thumbnails)
 8     },
 9     getFilesCode(fileList) {
10       var thumbnailBase64code = []
11       fileList.forEach((item, index) => {
12         var filecodeobj = { name: item.name, code: '' }
13         this.getBase64(item.raw).then(res => {
14           filecodeobj.code = res
15         })
16         thumbnailBase64code.push(filecodeobj)
17       })
18       return thumbnailBase64code
19     },
20     getBase64(file) {
21       return new Promise(function (resolve, reject) {
22         const reader = new FileReader()
23         let imgResult = ''
24         reader.readAsDataURL(file)
25         reader.onload = function () {
26           imgResult = reader.result
27         }
28         reader.onerror = function (error) {
29           reject(error)
30         }
31         reader.onloadend = function () {
32           resolve(imgResult)
33         }
34       })
35     }
View Code

  后端代码如下,base64版本稍有区别,就将文件对像换成了字符串类型,处理时多了一个步骤,将base64的字符串转换为字节流 

 1         /// <summary>
 2         /// 上传图片
 3         /// </summary>
 4         /// <param name="command">上传实体</param>
 5         /// <returns></returns>
 6         [HttpPost("UploadImage")]
 7         [RequestSizeLimit(10_000_000)]
 8         public IActionResult UploadImage([FromForm] UploadImageCommand command)
 9         {
10             var stream = command.File.OpenReadStream();
11             var fileName = command.File.FileName;
12             var result = application.CreateImage(command.Category, fileName, command.Info, stream);
13             return SuccessResult(result);
14         }
15 
16         /// <summary>
17         /// 上传视频
18         /// </summary>
19         /// <param name="command">上传视频的实体</param>
20         /// <returns></returns>
21         [HttpPost("UploadVideo")]
22         [RequestSizeLimit(100_000_000)]
23         public IActionResult UploadVideo([FromForm] UploadVideoCommand command)
24         {
25             var fileData = application.CreateVideo(command);
26             return SuccessResult(fileData);
27         }
28         /// <summary>
29         /// 上传视频
30         /// </summary>
31         /// <param name="command">上传视频的实体</param>
32         /// <returns></returns>
33         [HttpPost("UploadDocument")]
34         [RequestSizeLimit(100_000_000)]
35         public IActionResult UploadDocument([FromForm] UploadDocumentCommand command)
36         {
37             var fileData = application.CreateDocumnet(command);
38             return SuccessResult(fileData);
39         } 
View Code

  文件处理代码如下,文件保存我使用了注入机制,可以通过配置文件配置使用的文件保存方式 ,可以使用阿里云OSS等服务,也可以自定义,目前我使用的是自定义的服务

 1         /// <summary>
 2         /// 添加图片文件
 3         /// </summary>
 4         public FileResult CreateImage(int categoryId, string fileName, string info, Stream fileStream, bool compressImage = false, long supplierId = 0)
 5         {
 6             if(!AllowExtension(fileName))
 7                 throw new AVEException("不支持的文件格式");
 8             if(fileStream.Length > 0x300000)//3M
 9                 throw new AVEException("文件大小不能超过3M");
10             string fileext = Path.GetExtension(fileName).ToLower();
11             if(fileName.Length > 50)
12             {
13 
14                 fileName = fileName.Substring(0, 50 - fileext.Length) + fileext;
15             }
16             var file = new FileItem
17             {
18                 CategoryId = categoryId,
19                 Type = FileType.Image,
20                 Name = fileName,
21                 Info = info,
22                 Size = fileStream.Length,
23                 SupplierId = supplierId,
24                 CompressImage = compressImage
25             };
26             file.Extension = fileext;
27             (file.Path, file.ETag) = PutImage(file.Extension, fileStream); //保存图片
28             fileStream.Position = 0; //将文件流重新定位到开始,如果不写后面加载图片会报错
29             var image = Image.Load(fileStream);
30             file.Info = $"{image.Width}X{image.Height}";
31             repository.AddFile(file);
32             return file.Map<FileResult>();
33         }    
View Code

二、文件获取,如果是简单的获取文件服务器地址+文件路径即可,我这里主要是实现缩略图即时获取的功能,以及视频和文档等文件的权限控制

  1. 缩略图的即时生成,很多地方使用到缩略图,但是尺寸可能会变化,所以需要即时生成缩略图,但是也不能一直生成新的图片,所以需要判断图片是否存在,存在则直接访问,否则调用接口生成,如何判断图片是否存在呢,前端可以通过自定义指定判断,图片是否存在,不存在则调用接口获取,但是自定义指定在小程序中是无法使用的,所以我这边采用了后端接口处理,使用了.net6的拦截机制,将404请求拦截并且 判断请求的是不是图片,如果是图片并且是缩略图的请求,则将请求重写向到缩略图生成的接口地址,代码如下  

  1     // 拦截404请求
  2     app.Use((context, next) =>
  3 {
  4     var res = next(context);
  5     if(context.Response.StatusCode == 404 && context.Request.Method == "GET")
  6     {
  7         var path = context.Request.Path.ToString();
  8         var ext = Path.GetExtension(path);
  9         Console.WriteLine(ext);
 10         bool isRedirect = false;
 11         var redirectUrl = FileNotFoundExtensions.Get404ResourceUrl(context, out isRedirect);
 12         if(!isRedirect)
 13         {
 14             context.Response.StatusCode = 200;
 15             context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("404 页面没有找到" + context.Request.ContentType));
 16         }
 17         else
 18         {
 19             context.Response.Redirect(redirectUrl);
 20         }
 21     }
 22 
 23     return res;
 24 });
 25     // 404请求扩展类,处理404请求
 26      public static class FileNotFoundExtensions
 27     {
 28         /// <summary>
 29         /// 获取图片缩略图尺寸
 30         /// </summary>
 31         /// <param name="filenameArr"></param>
 32         /// <param name="path"></param>
 33         /// <returns></returns>
 34         private static List<int> GetThumSizes(string[] filenameArr, out string filename)
 35         {
 36             char[] splits = { '*', '_', '-', ',', ' ' };
 37             filename = string.Empty;
 38             var thumsizelist = new List<int>();
 39             var sizeArr = new List<string>();
 40             //取最后两个可能是尺寸的值
 41             if(filenameArr.Length > 2)
 42             {
 43                 sizeArr.Add(filenameArr[filenameArr.Length - 2]);
 44                 sizeArr.Add(filenameArr[filenameArr.Length - 1]);
 45             }
 46             else
 47             {
 48                 sizeArr.Add(filenameArr[filenameArr.Length - 1]);
 49             }
 50             for(var i = 1; i < filenameArr.Length - sizeArr.Count; i++)
 51             {
 52                 filename += "_" + filenameArr[i];
 53             }
 54             //两个值都是数字
 55             if(sizeArr.Count == 2)
 56             {
 57                 if(sizeArr[0].ToInt() > 0 && sizeArr[1].ToInt() > 0) //两个值都是数字
 58                 {
 59                     thumsizelist.Add(sizeArr[0].ToInt());
 60                     thumsizelist.Add(sizeArr[1].ToInt());
 61                 }
 62                 else if(sizeArr[1].ToInt() > 0) //最后一个是数字
 63                 {
 64                     thumsizelist.Add(sizeArr[1].ToInt());
 65                     filename += "_" + sizeArr[0];
 66                 }
 67                 else //最后一个不是数字,但是有可能是组合的尺寸值
 68                 {
 69                     filename += "_" + sizeArr[0];
 70                     var sublist = sizeArr[1].Split(splits);
 71                     if(sublist.Length == 2 && sublist[0].ToInt() > 0 && sublist[1].ToInt() > 0)
 72                     {
 73                         thumsizelist.Add(sublist[0].ToInt());
 74                         thumsizelist.Add(sublist[1].ToInt());
 75                     }
 76                 }
 77             }
 78             else
 79             {
 80                 if(sizeArr[0].ToInt() > 0)
 81                     thumsizelist.Add(sizeArr[0].ToInt());
 82                 else
 83                 {
 84                     var sublist = sizeArr[0].Split(splits);
 85                     if(sublist.Length == 2 && sublist[0].ToInt() > 0 && sublist[1].ToInt() > 0)
 86                     {
 87                         thumsizelist.Add(sublist[0].ToInt());
 88                         thumsizelist.Add(sublist[1].ToInt());
 89                     }
 90                 }
 91             }
 92             return thumsizelist;
 93         }
 94         public static string Get404ResourceUrl(HttpContext context, out bool isRedirect)
 95         {
 96 
 97             isRedirect = false;
 98             var path = context.Request.Path.ToString();
 99             var ext = Path.GetExtension(path);
100             Console.WriteLine(ext);
101             var resourceUrl = string.Empty;
102             if(FileHelper.IsImageExt(ext))
103             {
104                 resourceUrl = "/static/common/images/404.png";
105                 var pathArr = path.Split('/');
106                 var filename = pathArr[pathArr.Length - 1].UrlDecodeToNullString();
107                 var filenameArr = filename.Replace(ext, "").Split('_');
108 
109                 //如果是图片文件请求,并且带有缩略图大小
110                 string newpath = "";
111                 string newfilename = "";
112                 if(filenameArr.Length > 1)
113                 {
114                     var thumsizelist = GetThumSizes(filenameArr, out newfilename);
115                     //不包含尺寸,说明不是缩略图
116                     if(thumsizelist.IsEmpty())
117                     {
118                         isRedirect = true;
119                         return resourceUrl;
120                     }
121 
122                     var thumsize = string.Join(",", thumsizelist);
123                     for(var i = 0; i < pathArr.Length - 1; i++)
124                         newpath += (newpath.IsEmptyString() ? "" : "avepathsplit") + pathArr[i];
125                     newpath += "avepathsplit" + filenameArr[0] + newfilename + ext;
126                     isRedirect = true;
127                     return "/WebApi/common/" + thumsize + "/" + newpath;
128                     
129                 }
130                 else
131                 {
132                     isRedirect = true;
133                     return resourceUrl;
134                 }
135             }
136             else if(FileHelper.IsVideoExt(ext))
137             {
138                 isRedirect = true;
139                 return "/static/common/video/404.mp4";
140             }
141             else if(FileHelper.IsDocumentExt(ext))
142             {
143                 isRedirect = true;
144                 return "/static/common/images/404.png";
145                 
146             }
147             return "";
148         }
149     }
View Code

  缩略图生成方式,首先判断原图是否存在,如果不存在则返回404的图片,然后获取缩略图的尺寸,默认为指定宽度,如果需要同时指定宽度和高度可以使用*-_, 等字符分隔,比如400*800,400_800等表示宽400高800,如果要访问images/a.jpg,400*400的缩略图,可以使用 http://www.abc.com/webapi/common/400/images%2Fa.jpg,注意:如果路径中包含了/在请求接口时,接口可能会无法识别为参数,会识别为路径,所以需要将路径使用UrlEncode编码后再传给接口,使用nginx的需要注意,这里有一个坑,即使使用UrlEncode编码了路径 ,nginx仍然会将编码的路径解码,然后再转发给接口,接口仍然获取不到路径,我想了一个解决办法,就是不进行UrlEncode,面是将路径中的/替换为指定的字符串,然后接口中再替换回来,指定的字符串可以自定义,但是需要保存一般的路径中不会出现,否则可能会出现找不到文件的问题,接口代码如下:

  1          /// <summary>
  2         /// 访问图片
  3         /// </summary>
  4         /// <param name="size">所访问图片的大小,生成后图片的长和度都不会超过指定的大小,如果要指定宽度,可以宽度和高度之间使用-分隔,默认为长和宽大小都为指定大小</param>
  5         /// <param name="name">所要访问图片的名称或者相对地址</param>
  6         /// <returns>图片</returns>
  7         [HttpGet]
  8         [Route("{size}/{name}")]
  9         public IActionResult GetImage(string size, string name)
 10         {
 11             int thumWidth = 0, thumHeight = 0;
 12             char[] splits = { '*', '_', '-', ',', ' ' };
 13             size = size.UrlDecodeToNullString();
 14             var sizeArr = size.Split(splits);
 15             //
 16             if(sizeArr.Length > 1)
 17             {
 18                 thumWidth = sizeArr[0].ToInt();
 19                 thumHeight = sizeArr[1].ToInt();
 20             }
 21             else
 22             {
 23                 thumWidth = sizeArr[0].ToInt();
 24                 thumHeight = thumWidth;
 25             }
 26             var errorImage = "/static/common/images/404.png";//没有找到图片
 27             if(name.IsEmptyString())
 28             {
 29                 Logger.LogInformation($"图片名称为空:{name},size:{size}");
 30                 name = errorImage;
 31             }
 32             else
 33             {
 34                 name = HttpUtility.UrlDecode(name, Encoding.GetEncoding("utf-8"));
 35                 name = name.Replace("avepathsplit", "/").Replace("|", "/").Replace("/", "\\");
 36                 if(name.StartsWith("\\")) { name = name.Substring(1); }
 37             }
 38             var contentTypeStr = "image/jpeg";
 39             var ext = Path.GetExtension(name);
 40             //未知的图片类型
 41             if(!FileHelper.IsImageExt(ext))
 42             {
 43                 Logger.LogInformation($"不允许的扩展名:{ext},size:{size},name:{name}");
 44                 name = errorImage;
 45                 ext = ".ext";
 46             }
 47             else
 48             {
 49                 contentTypeStr = FileHelper.GetContentType(ext);
 50             }
 51             bool isThum = false;//是否是缩略图
 52             var thumname = name;//缩略图路径
 53             bool saveThum = false;
 54             //原图
 55             if(thumWidth <= 0 && thumHeight <= 0)
 56             {
 57                 using(var sw = objectStorage.GetObject(name))
 58                 {
 59                     var bytes = new byte[sw.Length];
 60                     sw.Read(bytes, 0, bytes.Length);
 61                     sw.Close();
 62                     Logger.LogInformation($"宽度和宽度小于等于0,返回原图");
 63                     return new FileContentResult(bytes, contentTypeStr);
 64                 }
 65             }
 66             else
 67             {
 68                 if(thumWidth == thumHeight)
 69                 {
 70                     thumname = name.Replace(ext, "_" + thumWidth + ext);
 71                 }
 72                 else
 73                 {
 74                     thumname = name.Replace(ext, "_" + thumWidth + "-" + thumHeight + ext);
 75                 }
 76 
 77                 isThum = true;
 78             }
 79 
 80             if(name != errorImage && !objectStorage.ExistObject(name))
 81             {
 82                 Logger.LogInformation($"图片不存在:{name},size:{size}");
 83                 name = errorImage;
 84             }
 85             if(objectStorage.ExistObject(thumname))
 86             {
 87                 using(var thumstream = objectStorage.GetObject(name))
 88                 {
 89                     var bytes = thumstream.ToByteArray();
 90                     thumstream.Close();
 91                     return new FileContentResult(bytes, contentTypeStr);
 92                 }
 93             }
 94             if(isThum && !objectStorage.ExistObject(thumname))
 95             {
 96                 saveThum = true;
 97             }
 98 
 99             //缩小图片
100             using(var stream = objectStorage.GetObject(name))
101             {
102                 var imgBmp = Image.Load(stream);
103                 //计算尺寸
104                 var oWidth = imgBmp.Width;
105                 var oHeight = imgBmp.Height;
106                 Tuple<int, int> sizes = GetThumSize(oWidth, oHeight, thumWidth, thumHeight);
107 
108                 stream.Position = 0;
109                 var ms = ImageHelper.GetThumbnailStream(stream, sizes.Item1, sizes.Item2, Path.GetExtension(name));
110 
111                 var bytes = ms.ToByteArray();
112                 if(saveThum)
113                 {
114                     ms.Position = 0;
115                     objectStorage.PutObject(thumname, ms);
116                 }
117                 else
118                 {
119                     Logger.LogInformation($"不保存缩略图name:{name},thumname:{thumname},size:{size}");
120                 }
121                 stream.Close();
122                 ms.Close();
123                 var obj = objectStorage.GetObject(thumname);
124                 return new FileContentResult(obj.ToByteArray(), contentTypeStr);
125             }
126         }    
View Code

  2. 视频和文档的访问控制实现,如果视频或者文件指定了需要授权才能访问,所以请求的时候不能直接返回地址,需要加鉴权的控制,如果没有授权则只能查看封面图片,授权之后可以查看内容,但是授权之后的返回的也不是具体的地址,不然将地址发给别人就无法控制了,我采用了token授权,token加了有效时间和会员身份的验证,即使用访问地址发给别人也无法访问,实现代码如下:

 1  /// <summary>
 2         /// 访问文件
 3         /// </summary>
 4         /// <param name="token">所要访问文件Id</param>
 5         /// <returns>文件内容</returns>
 6         [HttpGet]
 7         [Route("File/{token}")]
 8         public IActionResult GetFile(string token)
 9         {
10             long memberId = tokenHelper.GetUserTokenValue(token, "MemberId").ToLong();
11             long id = tokenHelper.GetUserTokenValue(token, "RequestKey").ToLong();
12             var fileitem = fileApplication.GetFileItem(id);
13             if(fileitem == null)
14             {
15                 return new BadRequestResult();
16             }
17             if(fileitem.AccessControl != 0)
18             {
19                 if(memberId > 0)
20                 {
21                     var accessapply = fileApplication.GetFileAccessApply(fileitem.Id, memberId);
22                     if(accessapply == null || accessapply.Status != Service.Common.Models.AccessApplyStatus.Audited)
23                     {
24                         return new BadRequestResult();
25                     }
26                 }
27                 else
28                 {
29                     return new BadRequestResult();
30                 }
31             }
32             var contentTypeStr = "image/jpeg";
33             var ext = Path.GetExtension(fileitem.Path);
34             contentTypeStr = FileHelper.GetContentType(ext);
35             using(var sw = objectStorage.GetObject(fileitem.Path))
36             {
37                 var bytes = new byte[sw.Length];
38                 sw.Read(bytes, 0, bytes.Length);
39                 sw.Close();
40                 return new FileContentResult(bytes, contentTypeStr);
41             }
42         }
View Code

 

 

posted on 2022-12-09 11:12  IT之家  阅读(235)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3