You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Aliyun.OSS.Util;
using Common.Enum;
using Common.Util;
using Entity.Base;
using Entity.DbModel.System.SysBaseObject;
using Entity.Dto.Req;
using Furion.VirtualFileServer;
using HybirdFrameworkCore.Autofac.Attribute;
using HybirdFrameworkCore.Configuration;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using OnceMi.AspNetCore.OSS;
using Repository.System;
using Service.Mgr;
using SqlSugar;
using Yitter.IdGenerator;
namespace Service.System
{
[Scope("SingleInstance")]
public class SysFileServices : BaseServices<SysFile>
{
private readonly SysFileRepository _sysFileRep;
private readonly SysUserRepository _sysUserRep;
private readonly UploadOptions _uploadOptions;
private readonly IOSSService _OSSService;
private readonly string _imageType = ".jpg.png.bmp.gif.tif";
public SysFileServices(
SysUserRepository sysUserRepository,
SysFileRepository sysFileRep,
IOSSServiceFactory ossServiceFactory)
{
_sysUserRep = sysUserRepository;
_sysFileRep = sysFileRep;
_uploadOptions = new UploadOptions
{
Path = AppSettingsHelper.GetContent("Upload", "Path"),
MaxSize = Convert.ToInt64(AppSettingsHelper.GetContent("Upload", "MaxSize")),
ContentType = new List<string> { "image/jpg", "image/png", "image/jpeg", "image/gif", "image/bmp", "text/plain", "application/pdf", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "video/mp4" },
EnableMd5 = false,
};
}
/// <summary>
/// 上传头像 🔖
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
[DisplayName("上传头像")]
public async Task<SysFile> UploadAvatar([Required] IFormFile file, string path)
{
var sysFile = await HandleUploadFile(file, path, _imageType);
var user = _sysUserRep.QueryByClause(u => u.Id == UserManager.UserId);
await _sysUserRep.UpdateAsync(u => new SysUser() { Avatar = sysFile.Url }, u => u.Id == user.Id);
return sysFile;
}
/// <summary>
/// 获取文件分页列表 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("获取文件分页列表")]
public async Task<SqlSugarPagedList<SysFile>> Page(PageFileReq input)
{
RefAsync<int> total = 0;
var items = await _sysFileRep.QueryPageAsync(
!string.IsNullOrWhiteSpace(input.FileName), u => u.FileName.Contains(input.FileName.Trim()),
!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()),
u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime,
u => u.CreateTime, input.Page, input.PageSize, total);
return SqlSugarPagedExtensions.CreateSqlSugarPagedList(items, total, input.Page, input.PageSize);
}
/// <summary>
/// 上传文件 🔖
/// </summary>
/// <param name="file"></param>
/// <param name="path"></param>
/// <returns></returns>
[DisplayName("上传文件")]
public async Task<SysFile> UploadFile([Required] IFormFile file, [FromQuery] string? path)
{
return await HandleUploadFile(file, path);
}
/// <summary>
/// 上传文件Base64
/// </summary>
/// <param name="strBase64"></param>
/// <param name="fileName"></param>
/// <param name="contentType"></param>
/// <param name="path"></param>
/// <returns></returns>
private async Task<SysFile> UploadFileFromBase64(string strBase64, string fileName, string contentType, string? path)
{
byte[] fileData = Convert.FromBase64String(strBase64);
var ms = new MemoryStream();
ms.Write(fileData);
ms.Seek(0, SeekOrigin.Begin);
if (string.IsNullOrEmpty(fileName))
fileName = $"{YitIdHelper.NextId()}.jpg";
if (string.IsNullOrEmpty(contentType))
contentType = "image/jpg";
IFormFile formFile = new FormFile(ms, 0, fileData.Length, "file", fileName)
{
Headers = new HeaderDictionary(),
ContentType = contentType
};
return await UploadFile(formFile, path);
}
/// <summary>
/// 上传文件Base64 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("上传文件Base64")]
[HttpPost]
public async Task<SysFile> UploadFileFromBase64(UploadFileFromBase64Req input)
{
return await UploadFileFromBase64(input.FileDataBase64, input.FileName, input.ContentType, input.Path);
}
/// <summary>
/// 上传多文件 🔖
/// </summary>
/// <param name="files"></param>
/// <returns></returns>
[DisplayName("上传多文件")]
public async Task<List<SysFile>> UploadFiles([Required] List<IFormFile> files)
{
var filelist = new List<SysFile>();
foreach (var file in files)
{
filelist.Add(await UploadFile(file, ""));
}
return filelist;
}
///// <summary>
///// 根据文件Id或Url下载 🔖
///// </summary>
///// <param name="input"></param>
///// <returns></returns>
//[DisplayName("根据文件Id或Url下载")]
//public async Task<IActionResult> DownloadFile(FileReq input)
//{
// var file = input.Id > 0 ? await GetFile(input) : await _sysFileRep.QueryByClauseAsync(u => u.Url == input.Url);
// var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
// if (_OSSProviderOptions.IsEnable)
// {
// var filePath = string.Concat(file.FilePath, "/", file.Id.ToString() + file.Suffix);
// var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName.ToString(), filePath, 5)).GetAsStreamAsync();
// return new FileStreamResult(stream.Stream, "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
// }
// else
// {
// var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix);
// var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
// return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
// }
//}
/// <summary>
/// 更新文件 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[ApiDescriptionSettings(Name = "Update"), HttpPost]
[DisplayName("更新文件")]
public async Task<string> UpdateFile(FileReq input)
{
string result = "";
var isExist = await _sysFileRep.QueryByClauseAsync(u => u.Id == input.Id);
if (isExist == null)
result = "文件不存在";
bool updateResult = await _sysFileRep.UpdateAsync(u => new SysFile() { FileName = input.FileName }, u => u.Id == input.Id);
if (updateResult)
{
result = "更新文件成功";
}
return result;
}
/// <summary>
/// 获取文件
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private async Task<SysFile> GetFile([FromQuery] FileReq input)
{
var file = await _sysFileRep.QueryByClauseAsync(u => u.Id == input.Id);
if (file==null)
{
//文件不存在
return new SysFile();
}
return file;
}
/// <summary>
/// 上传文件
/// </summary>
/// <param name="file">文件</param>
/// <param name="savePath">路径</param>
/// <param name="allowSuffix">允许格式:.jpg.png.gif.tif.bmp</param>
/// <returns></returns>
private async Task<SysFile> HandleUploadFile(IFormFile file, string savePath, string allowSuffix = "")
{
if (file == null)
throw new ArgumentException($"文件不存在");//文件不存在
// 判断是否重复上传的文件
var sizeKb = (long)(file.Length / 1024.0); // 大小KB
var fileMd5 = string.Empty;
if (_uploadOptions.EnableMd5)
{
using (var fileStream = file.OpenReadStream())
{
fileMd5 = OssUtils.ComputeContentMd5(fileStream, fileStream.Length);
}
/*
* Mysql8 中如果使用了 utf8mb4_general_ci 之外的编码会出错,尽量避免在条件里使用.ToString()
* 因为 Squsugar 并不是把变量转换为字符串来构造SQL语句而是构造了CAST(123 AS CHAR)这样的语句这样这个返回值是utf8mb4_general_ci所以容易出错。
*/
var strSizeKb = sizeKb.ToString();
var sysFile = await _sysFileRep.QueryByClauseAsync(u => u.FileMd5 == fileMd5 && (u.SizeKb == null || u.SizeKb == strSizeKb));
if (sysFile != null) return sysFile;
}
var path = savePath;
if (string.IsNullOrWhiteSpace(savePath))
{
path = _uploadOptions.Path;
var reg = new Regex(@"(\{.+?})");
var match = reg.Matches(path);
match.ToList().ForEach(a =>
{
var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2)); // 每天一个目录
path = path.Replace(a.ToString(), str);
});
}
// 验证文件类型
if (!_uploadOptions.ContentType.Contains(file.ContentType))
throw new ArgumentException($"不允许的文件类型");
// 验证文件大小
if (sizeKb > _uploadOptions.MaxSize)
throw new ArgumentException($"文件超过允许大小");
// 获取文件后缀
var suffix = Path.GetExtension(file.FileName).ToLower(); // 后缀
if (string.IsNullOrWhiteSpace(suffix))
{
var contentTypeProvider = FS.GetFileExtensionContentTypeProvider();
suffix = contentTypeProvider.Mappings.FirstOrDefault(u => u.Value == file.ContentType).Key;
// 修改 image/jpeg 类型返回的 .jpe 后缀
if (suffix == ".jpe")
suffix = ".jpg";
}
if (string.IsNullOrWhiteSpace(suffix))
throw new ArgumentException($"文件后缀错误");
var newFile = new SysFile
{
//Id = YitIdHelper.NextId(),
FileName = Path.GetFileNameWithoutExtension(file.FileName),
Suffix = suffix,
SizeKb = sizeKb.ToString(),
FileMd5 = fileMd5,
};
var finalName = newFile.Id + suffix; // 文件最终名称
string time = DateTime.Now.ToString("yyyyMMdd");
var filePath = Path.Combine(savePath, time);
if (!Directory.Exists(filePath))
Directory.CreateDirectory(filePath);
filePath = Path.Combine(filePath, finalName);
// 使用FileStream保存文件
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
newFile.FilePath = filePath;
newFile.Url = AppSettingsHelper.GetContent("HttpContextRequest", "Scheme") + "/" + time + "/" + finalName;
await _sysFileRep.InsertAsync(newFile);
SysFile newAddFile = await _sysFileRep.QueryByClauseAsync(u => u.FilePath == filePath);
if (newAddFile != null)
{
newFile.Id = newAddFile.Id;
return newFile;
}
else
return null;
}
}
}