优化文件存储

This commit is contained in:
不做码农 2022-03-10 21:39:46 +08:00
parent 3555833ae7
commit eb32117b8c
9 changed files with 57 additions and 108 deletions

View File

@ -7,9 +7,11 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Snowflake.Core;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using ZR.Admin.WebApi.Extensions; using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Common; using ZR.Common;
@ -158,25 +160,29 @@ namespace ZR.Admin.WebApi.Controllers
[HttpPost] [HttpPost]
[Verify] [Verify]
[ActionPermissionFilter(Permission = "common")] [ActionPermissionFilter(Permission = "common")]
public IActionResult UploadFileAliyun([FromForm(Name = "file")] IFormFile formFile, string fileName = "", string fileDir = "") public async Task<IActionResult> UploadFileAliyun([FromForm(Name = "file")] IFormFile formFile, string fileName = "", string fileDir = "")
{ {
if (fileDir.IsEmpty()) fileDir = "uploads";
if (formFile == null) throw new CustomException(ResultCode.PARAM_ERROR, "上传文件不能为空"); if (formFile == null) throw new CustomException(ResultCode.PARAM_ERROR, "上传文件不能为空");
string fileExt = Path.GetExtension(formFile.FileName); string fileExt = Path.GetExtension(formFile.FileName);//文件后缀
string[] AllowedFileExtensions = new string[] { ".jpg", ".gif", ".png", ".jpeg", ".webp", ".svga", ".xls", ".doc", ".zip", ".json", ".txt", ".bundle" }; double fileSize = formFile.Length / 1024.0;//文件大小KB
int MaxContentLength = 1024 * 1024 * 15; string[] NotAllowedFileExtensions = new string[] { ".bat", ".exe", ".jar", ".js" };
double fileSize = formFile.Length / 1024; int MaxContentLength = 15;
if (!AllowedFileExtensions.Contains(fileExt)) if (NotAllowedFileExtensions.Contains(fileExt))
{ {
return ToResponse(ResultCode.CUSTOM_ERROR, "上传失败,未经允许上传类型"); return ToResponse(ResultCode.CUSTOM_ERROR, "上传失败,未经允许上传类型");
} }
if ((fileSize / 1024) > MaxContentLength)
if (formFile.Length > MaxContentLength)
{ {
return ToResponse(ResultCode.CUSTOM_ERROR, "上传文件过大,不能超过 " + (MaxContentLength / 1024).ToString() + " MB"); return ToResponse(ResultCode.CUSTOM_ERROR, "上传文件过大,不能超过 " + MaxContentLength + " MB");
} }
(bool, string, string) result = SysFileService.SaveFile(fileDir, formFile, fileName); (bool, string, string) result = new();
long fileId = SysFileService.InsertFile(new SysFile() await Task.Run(() =>
{
result = SysFileService.SaveFile(fileDir, formFile, fileName, "");
});
long id = SysFileService.InsertFile(new SysFile()
{ {
AccessUrl = result.Item2, AccessUrl = result.Item2,
Create_by = HttpContext.GetName(), Create_by = HttpContext.GetName(),
@ -193,7 +199,7 @@ namespace ZR.Admin.WebApi.Controllers
{ {
url = result.Item2, url = result.Item2,
fileName = result.Item3, fileName = result.Item3,
fileId fileId = id
}); });
} }
#endregion #endregion

View File

@ -57,74 +57,13 @@ namespace ZR.Admin.WebApi.Controllers
/// <returns></returns> /// <returns></returns>
[HttpGet("{Id}")] [HttpGet("{Id}")]
[ActionPermissionFilter(Permission = "tool:file:query")] [ActionPermissionFilter(Permission = "tool:file:query")]
public IActionResult GetSysFile(int Id) public IActionResult GetSysFile(long Id)
{ {
var response = _SysFileService.GetFirst(x => x.Id == Id); var response = _SysFileService.GetFirst(x => x.Id == Id);
return SUCCESS(response); return SUCCESS(response);
} }
///// <summary>
///// 添加文件存储
///// </summary>
///// <returns></returns>
//[HttpPost]
//[ActionPermissionFilter(Permission = "tool:file:add")]
//[Log(Title = "文件存储", BusinessType = BusinessType.INSERT)]
//public IActionResult AddSysFile([FromBody] SysFileDto parm)
//{
// if (parm == null)
// {
// throw new CustomException("请求参数错误");
// }
// //从 Dto 映射到 实体
// var model = parm.Adapt<SysFile>().ToCreate(HttpContext);
// var response = _SysFileService.Insert(model, it => new
// {
// it.FileName,
// it.FileUrl,
// it.StorePath,
// it.FileSize,
// it.FileExt,
// it.Create_by,
// it.Create_time,
// it.StoreType,
// it.AccessUrl,
// });
// return ToResponse(response);
//}
///// <summary>
///// 更新文件存储
///// </summary>
///// <returns></returns>
//[HttpPut]
//[ActionPermissionFilter(Permission = "tool:file:update")]
//[Log(Title = "文件存储", BusinessType = BusinessType.UPDATE)]
//public IActionResult UpdateSysFile([FromBody] SysFileDto parm)
//{
// if (parm == null)
// {
// throw new CustomException("请求实体不能为空");
// }
// //从 Dto 映射到 实体
// var model = parm.Adapt<SysFile>().ToUpdate(HttpContext);
// var response = _SysFileService.Update(w => w.Id == model.Id, it => new SysFile()
// {
// //Update 字段映射
// FileUrl = model.FileUrl,
// StorePath = model.StorePath,
// FileSize = model.FileSize,
// FileExt = model.FileExt,
// StoreType = model.StoreType,
// AccessUrl = model.AccessUrl,
// });
// return ToResponse(response);
//}
/// <summary> /// <summary>
/// 删除文件存储 /// 删除文件存储
/// </summary> /// </summary>
@ -134,7 +73,7 @@ namespace ZR.Admin.WebApi.Controllers
[Log(Title = "文件存储", BusinessType = BusinessType.DELETE)] [Log(Title = "文件存储", BusinessType = BusinessType.DELETE)]
public IActionResult DeleteSysFile(string ids) public IActionResult DeleteSysFile(string ids)
{ {
int[] idsArr = Tools.SpitIntArrary(ids); long[] idsArr = Tools.SpitLongArrary(ids);
if (idsArr.Length <= 0) { return ToResponse(ApiResult.Error($"删除失败Id 不能为空")); } if (idsArr.Length <= 0) { return ToResponse(ApiResult.Error($"删除失败Id 不能为空")); }
var response = _SysFileService.Delete(idsArr); var response = _SysFileService.Delete(idsArr);

View File

@ -21,7 +21,7 @@ namespace ZR.Common
/// <param name="bucketName">存储桶 如果为空默认取配置文件</param> /// <param name="bucketName">存储桶 如果为空默认取配置文件</param>
public static System.Net.HttpStatusCode PutObjectFromFile(Stream filestreams, string dirPath, string bucketName = "") public static System.Net.HttpStatusCode PutObjectFromFile(Stream filestreams, string dirPath, string bucketName = "")
{ {
OssClient client = new OssClient(endpoint, accessKeyId, accessKeySecret); OssClient client = new(endpoint, accessKeyId, accessKeySecret);
if (string.IsNullOrEmpty(bucketName)) { bucketName = bucketName1; } if (string.IsNullOrEmpty(bucketName)) { bucketName = bucketName1; }
try try
{ {

View File

@ -9,7 +9,7 @@ namespace ZR.Model.System.Dto
/// </summary> /// </summary>
public class SysFileDto public class SysFileDto
{ {
public int Id { get; set; } public long Id { get; set; }
public string FileName { get; set; } public string FileName { get; set; }
public string FileUrl { get; set; } public string FileUrl { get; set; }
public string StorePath { get; set; } public string StorePath { get; set; }
@ -25,6 +25,6 @@ namespace ZR.Model.System.Dto
public DateTime? BeginCreate_time { get; set; } public DateTime? BeginCreate_time { get; set; }
public DateTime? EndCreate_time { get; set; } public DateTime? EndCreate_time { get; set; }
public int? StoreType { get; set; } public int? StoreType { get; set; }
public int? FileId { get; set; } public long? FileId { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using SqlSugar; using Newtonsoft.Json;
using SqlSugar;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
@ -13,7 +14,8 @@ namespace ZR.Model.System
/// 描述 : 自增id /// 描述 : 自增id
/// 空值 : false /// 空值 : false
/// </summary> /// </summary>
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)] [JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(IsPrimaryKey = true)]
public long Id { get; set; } public long Id { get; set; }
/// <summary> /// <summary>
/// 文件真实名 /// 文件真实名

View File

@ -16,7 +16,7 @@ namespace ZR.Service.System.IService
/// <param name="formFile"></param> /// <param name="formFile"></param>
/// <returns>结果、地址、文件名</returns> /// <returns>结果、地址、文件名</returns>
(bool, string, string) SaveFile(string picdir, IFormFile formFile); (bool, string, string) SaveFile(string picdir, IFormFile formFile);
(bool, string, string) SaveFile(string picdir, IFormFile formFile, string customFileName); (bool, string, string) SaveFile(string picdir, IFormFile formFile, string customFileName, string bucketName);
/// <summary> /// <summary>
/// 按时间来创建文件夹 /// 按时间来创建文件夹
/// </summary> /// </summary>

View File

@ -11,6 +11,7 @@ using System.Net;
using ZR.Model.System; using ZR.Model.System;
using ZR.Repository.System; using ZR.Repository.System;
using Infrastructure.Extensions; using Infrastructure.Extensions;
using SqlSugar.DistributedSystem.Snowflake;
namespace ZR.Service.System namespace ZR.Service.System
{ {
@ -36,18 +37,28 @@ namespace ZR.Service.System
/// <returns></returns> /// <returns></returns>
public (bool, string, string) SaveFile(string picdir, IFormFile formFile) public (bool, string, string) SaveFile(string picdir, IFormFile formFile)
{ {
return SaveFile(picdir, formFile, ""); return SaveFile(picdir, formFile, "", "");
} }
public (bool, string, string) SaveFile(string picdir, IFormFile formFile, string customFileName)
/// <summary>
/// 存储文件
/// </summary>
/// <param name="picdir">文件夹</param>
/// <param name="formFile"></param>
/// <param name="customFileName">自定义文件名</param>
/// <param name="bucketName">存储桶</param>
/// <returns></returns>
public (bool, string, string) SaveFile(string picdir, IFormFile formFile, string customFileName, string bucketName)
{ {
// eg: uploads/2020/08/18 // eg: uploads/2020/08/18
string dir = GetdirPath(picdir.ToString()); //string dir = GetdirPath(picdir.ToString());
string tempName = customFileName.IsEmpty() ? HashFileName() : customFileName; string tempName = customFileName.IsEmpty() ? HashFileName() : customFileName;
string fileExt = Path.GetExtension(formFile.FileName); string fileExt = Path.GetExtension(formFile.FileName);
string fileName = $"{tempName}{fileExt}"; string fileName = tempName + fileExt;
string webUrl = $"{domainUrl}/{dir}/{fileName}"; string webUrl = string.Concat(domainUrl, "/", picdir, "/", fileName);
HttpStatusCode statusCode = AliyunOssHelper.PutObjectFromFile(formFile.OpenReadStream(), Path.Combine(dir, fileName)); HttpStatusCode statusCode = AliyunOssHelper.PutObjectFromFile(formFile.OpenReadStream(), Path.Combine(picdir, fileName), bucketName);
return (statusCode == HttpStatusCode.OK, webUrl, fileName); return (statusCode == HttpStatusCode.OK, webUrl, fileName);
} }
@ -81,13 +92,13 @@ namespace ZR.Service.System
{ {
try try
{ {
return InsertReturnBigIdentity(file); return Insertable(file).ExecuteReturnSnowflakeId();//单条插入返回雪花ID;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("存储图片失败" + ex.Message); Console.WriteLine("存储图片失败" + ex.Message);
throw new Exception(ex.Message);
} }
return 1;
} }
} }
} }

View File

@ -228,7 +228,7 @@ export default {
margin-right: 10px; margin-right: 10px;
} }
::v-deep .el-upload-dragger { ::v-deep .el-upload-dragger {
width: 220px; width: 270px;
height: 150px; height: 150px;
} }
</style> </style>

View File

@ -34,15 +34,13 @@
<!-- 数据区域 --> <!-- 数据区域 -->
<el-table :data="dataList" v-loading="loading" ref="table" border highlight-current-row @selection-change="handleSelectionChange"> <el-table :data="dataList" v-loading="loading" ref="table" border highlight-current-row @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" /> <el-table-column type="selection" width="50" align="center" />
<el-table-column prop="id" label="文件id" align="center" width="80" /> <el-table-column prop="id" label="文件id" align="center" width="180" />
<el-table-column prop="fileName" label="文件名" align="center"> <el-table-column prop="fileName" label="文件名" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-popover :content="scope.row.fileUrl" placement="top-start" title="路径" trigger="hover">
<a slot="reference" :href="scope.row.accessUrl" class="el-link--primary" <a slot="reference" :href="scope.row.accessUrl" class="el-link--primary"
style="word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color: #1890ff;font-size: 13px;" target="_blank"> style="word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color: #1890ff;font-size: 13px;" target="_blank">
{{ scope.row.fileName }} {{ scope.row.fileName }}
</a> </a>
</el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="accessUrl" align="center" label="预览图" width="100"> <el-table-column prop="accessUrl" align="center" label="预览图" width="100">
@ -73,11 +71,11 @@
<pagination class="mt10" background :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> <pagination class="mt10" background :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改文件存储对话框 --> <!-- 添加或修改文件存储对话框 -->
<el-dialog :title="title" :lock-scroll="false" :visible.sync="open" width="380px"> <el-dialog :title="title" :lock-scroll="false" :visible.sync="open" width="320px">
<el-form ref="form" :model="form" :rules="rules" label-width="100px" label-position="left"> <el-form ref="form" :model="form" :rules="rules" label-position="left">
<el-row> <el-row>
<el-col :lg="24"> <el-col :lg="24">
<el-form-item label="存储类型" prop="storeType"> <el-form-item prop="storeType">
<el-select v-model="form.storeType" placeholder="请选择存储类型" @change="handleSelectStore"> <el-select v-model="form.storeType" placeholder="请选择存储类型" @change="handleSelectStore">
<el-option v-for="item in storeTypeOptions" :key="item.dictValue" :label="item.dictLabel" :value="parseInt(item.dictValue)"> <el-option v-for="item in storeTypeOptions" :key="item.dictValue" :label="item.dictLabel" :value="parseInt(item.dictValue)">
</el-option> </el-option>
@ -85,32 +83,25 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :lg="24"> <el-col :lg="24">
<el-form-item prop="storePath"> <el-form-item prop="">
<span slot="label"> <el-input v-model="form.storePath" placeholder="请输入存储文件夹" clearable="" auto-complete="" />
文件夹前缀
<el-tooltip content="比如存储到'/uploads' '如果不填写默认按时间存储eg/2021/12/16(固定段)'" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-input v-model="form.storePath" placeholder="请输入文件夹前缀" clearable="" auto-complete="" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :lg="24"> <el-col :lg="24">
<el-form-item label="自定文件名" prop="fileName"> <el-form-item prop="fileName">
<el-input v-model="form.fileName" placeholder="请输入文件名" clearable="" /> <el-input v-model="form.fileName" placeholder="请输入文件名" clearable="" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :lg="24"> <el-col :lg="24">
<el-form-item label="" prop="accessUrl"> <el-form-item prop="accessUrl">
<UploadFile v-model="form.accessUrl" :uploadUrl="uploadUrl" :fileType="[]" :limit="1" :fileSize="15" :drag="true" <UploadFile v-model="form.accessUrl" :uploadUrl="uploadUrl" :fileType="[]" :limit="1" :fileSize="15" :drag="true"
:data="{ 'fileDir' : form.storePath, 'fileName': form.fileName}" column="accessUrl" @input="handleUploadSuccess" /> :data="{ 'fileDir' : form.storePath, 'fileName': form.fileName}" column="accessUrl" @input="handleUploadSuccess" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button> <el-button type="text" @click="cancel"> </el-button>
</div> </div>
</el-dialog> </el-dialog>