mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-04-24 07:02:51 +08:00
refactor: ♻️ 文件上传功能调整
This commit is contained in:
parent
6fed7ec752
commit
e0a4a40314
@ -15,7 +15,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.13.2">
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.2-alpha">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"version": "2.3.1",
|
||||
"devDependencies": {
|
||||
"cz-git": "^1.11.0",
|
||||
"cz-git": "^1.11.1",
|
||||
"commitizen": "^4.3.1",
|
||||
"prettier": "^3.5.0",
|
||||
"prettier": "^3.5.3",
|
||||
"standard-version": "^9.5.0"
|
||||
},
|
||||
"config": {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 2f58f3291ae7a31bf935ed26d23fd587d181fb2b
|
||||
Subproject commit b16b1a35559281a9f53d6914f1195a204c9b4185
|
@ -1 +1 @@
|
||||
Subproject commit f5509a75ffb26175f7024fb609579050f6f5250d
|
||||
Subproject commit 15717df4499d284ec77c624a281c199a29280a4e
|
@ -1 +1 @@
|
||||
Subproject commit d4a92aca6f18b68875126f1974baa15fd2ab1fab
|
||||
Subproject commit 322cbc32df1363755d4b0588923ee7324676c3b6
|
@ -1,4 +1,4 @@
|
||||
#r "nuget: NSExt, 2.3.3"
|
||||
#r "nuget: NSExt, 2.3.4"
|
||||
using NSExt.Extensions;
|
||||
|
||||
Console.WriteLine("请输入原始名称(NetAdmin):");
|
||||
|
@ -0,0 +1,17 @@
|
||||
namespace NetAdmin.Domain.Dto.Sys.File;
|
||||
|
||||
/// <summary>
|
||||
/// 响应:文件上传
|
||||
/// </summary>
|
||||
public record UploadFileRsp : DataAbstraction
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
public string FileName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 可访问的url地址
|
||||
/// </summary>
|
||||
public string Url { get; init; }
|
||||
}
|
@ -5,7 +5,7 @@ namespace NetAdmin.Domain.Dto.Sys.Job;
|
||||
/// </summary>
|
||||
public sealed record EditJobReq : CreateJobReq
|
||||
{
|
||||
/// <inheritdoc cref="System.Version" />
|
||||
/// <inheritdoc cref="IFieldVersion.Version" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Version { get; init; }
|
||||
}
|
@ -11,7 +11,7 @@ public sealed record SetAvatarReq : Sys_User
|
||||
[Url(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.网络地址不正确))]
|
||||
public override string Avatar { get; init; }
|
||||
|
||||
/// <inheritdoc cref="System.Version" />
|
||||
/// <inheritdoc cref="IFieldVersion.Version" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Version { get; init; }
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<ProjectReference Include="../NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CronExpressionDescriptor" Version="2.38.0"/>
|
||||
<PackageReference Include="CronExpressionDescriptor" Version="2.39.0"/>
|
||||
<PackageReference Include="Cronos" Version="0.9.0"/>
|
||||
<PackageReference Include="NetAdmin.CsvHelper" Version="1.0.0"/>
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14"/>
|
||||
|
@ -34,7 +34,6 @@ public sealed class ExampleController(IExampleCache cache) : ControllerBase<IExa
|
||||
/// <summary>
|
||||
/// 示例分组计数
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryExampleReq> req)
|
||||
{
|
||||
return Cache.CountByAsync(req);
|
||||
|
@ -5,10 +5,10 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NetAdmin.FreeSql.DbContext" Version="1.1.2" Label="refs"/>
|
||||
<PackageReference Include="NetAdmin.FreeSql.Provider.Sqlite" Version="1.1.2" Label="refs"/>
|
||||
<PackageReference Include="Gurion" Version="1.2.11" Label="refs"/>
|
||||
<PackageReference Include="Gurion" Version="1.2.12" Label="refs"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.2"/>
|
||||
<PackageReference Include="Minio" Version="6.0.4"/>
|
||||
<PackageReference Include="NSExt" Version="2.3.3"/>
|
||||
<PackageReference Include="NSExt" Version="2.3.4"/>
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.5"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -15,8 +15,8 @@ public sealed class MinioHelper(IOptions<UploadOptions> uploadOptions) : IScoped
|
||||
/// <param name="fileStream">文件流</param>
|
||||
/// <param name="contentType">文件类型</param>
|
||||
/// <param name="fileSize">文件大小</param>
|
||||
/// <returns>可访问的url地址</returns>
|
||||
public async Task<string> UploadAsync(string objectName, Stream fileStream, string contentType, long fileSize)
|
||||
/// <returns>文件名,可访问的url地址</returns>
|
||||
public async Task<(string FileName, string Url)> UploadAsync(string objectName, Stream fileStream, string contentType, long fileSize)
|
||||
{
|
||||
using var minio = new MinioClient().WithEndpoint(uploadOptions.Value.Minio.ServerAddress)
|
||||
.WithCredentials( //
|
||||
@ -38,6 +38,6 @@ public sealed class MinioHelper(IOptions<UploadOptions> uploadOptions) : IScoped
|
||||
.WithContentType(contentType);
|
||||
_ = await minio.PutObjectAsync(putArgs).ConfigureAwait(false);
|
||||
|
||||
return $"{uploadOptions.Value.Minio.AccessUrl}/{uploadOptions.Value.Minio.BucketName}/{objectName}";
|
||||
return (objectName, $"{uploadOptions.Value.Minio.AccessUrl}/{uploadOptions.Value.Minio.BucketName}/{objectName}");
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using NetAdmin.Domain.Dto.Sys.File;
|
||||
|
||||
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
||||
|
||||
/// <summary>
|
||||
@ -8,5 +10,5 @@ public interface IFileModule
|
||||
/// <summary>
|
||||
/// 文件上传
|
||||
/// </summary>
|
||||
Task<string> UploadAsync(IFormFile file);
|
||||
Task<UploadFileRsp> UploadAsync(IFormFile file);
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using NetAdmin.Domain.Dto.Sys.File;
|
||||
|
||||
namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||
|
||||
/// <inheritdoc cref="IFileService" />
|
||||
@ -8,7 +10,7 @@ public sealed class FileService(IOptions<UploadOptions> uploadOptions, MinioHelp
|
||||
/// <exception cref="NetAdminInvalidOperationException">文件不能为空</exception>
|
||||
/// <exception cref="NetAdminInvalidOperationException">允许上传的文件格式</exception>
|
||||
/// <exception cref="NetAdminInvalidOperationException">允许的文件大小</exception>
|
||||
public async Task<string> UploadAsync(IFormFile file)
|
||||
public async Task<UploadFileRsp> UploadAsync(IFormFile file)
|
||||
{
|
||||
if (file == null || file.Length < 1) {
|
||||
throw new NetAdminInvalidOperationException(Ln.文件不能为空);
|
||||
@ -22,9 +24,9 @@ public sealed class FileService(IOptions<UploadOptions> uploadOptions, MinioHelp
|
||||
throw new NetAdminInvalidOperationException($"{Ln.允许的文件大小} {uploadOptions.Value.MaxSize}");
|
||||
}
|
||||
|
||||
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
|
||||
var objectName = $"{UserToken.Id}/{fileName}";
|
||||
var objectName = $"{UserToken.Id}/{file.FileName}";
|
||||
await using var fs = file.OpenReadStream();
|
||||
return await minioHelper.UploadAsync(objectName, fs, file.ContentType, file.Length).ConfigureAwait(false);
|
||||
var (fileName, url) = await minioHelper.UploadAsync(objectName, fs, file.ContentType, file.Length).ConfigureAwait(false);
|
||||
return new UploadFileRsp { FileName = fileName, Url = url };
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using NetAdmin.Domain.Dto.Sys.File;
|
||||
|
||||
namespace NetAdmin.SysComponent.Cache.Sys;
|
||||
|
||||
/// <inheritdoc cref="IFileCache" />
|
||||
@ -5,7 +7,7 @@ public sealed class FileCache(IDistributedCache cache, IFileService service) //
|
||||
: DistributedCache<IFileService>(cache, service), IScoped, IFileCache
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<string> UploadAsync(IFormFile file)
|
||||
public Task<UploadFileRsp> UploadAsync(IFormFile file)
|
||||
{
|
||||
return Service.UploadAsync(file);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
using NetAdmin.Domain.Dto.Sys.File;
|
||||
|
||||
namespace NetAdmin.SysComponent.Host.Controllers.Sys;
|
||||
|
||||
/// <summary>
|
||||
@ -10,7 +12,7 @@ public sealed class FileController(IFileCache cache) : ControllerBase<IFileCache
|
||||
/// <summary>
|
||||
/// 文件上传
|
||||
/// </summary>
|
||||
public Task<string> UploadAsync(IFormFile file)
|
||||
public Task<UploadFileRsp> UploadAsync(IFormFile file)
|
||||
{
|
||||
return Cache.UploadAsync(file);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.2"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.3"/>
|
||||
<PackageReference Include="xunit" Version="2.9.3"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
@ -1,3 +1,5 @@
|
||||
using NetAdmin.Domain.Dto.Sys.File;
|
||||
|
||||
namespace UnitTests.Sys;
|
||||
|
||||
/// <summary>
|
||||
@ -11,7 +13,7 @@ public class FileTests(WebTestApplicationFactory<Startup> factory, ITestOutputHe
|
||||
/// <inheritdoc />
|
||||
[InlineData(null)]
|
||||
[Theory]
|
||||
public async Task<string> UploadAsync(IFormFile file)
|
||||
public async Task<UploadFileRsp> UploadAsync(IFormFile file)
|
||||
{
|
||||
var rsp = await PostJsonAsync(typeof(FileController), file);
|
||||
Assert.True(rsp.IsSuccessStatusCode);
|
||||
|
@ -52,9 +52,9 @@ public abstract class WebApiTestBase<T>(WebTestApplicationFactory<T> factory, IT
|
||||
var req = new LoginByPwdReq //
|
||||
{
|
||||
Password
|
||||
= Environment.GetEnvironmentVariable(nameof(WebTestApplicationFactory<T>.TEST_PASSWORD)) ??
|
||||
= Environment.GetEnvironmentVariable(nameof(WebTestApplicationFactory<>.TEST_PASSWORD)) ??
|
||||
WebTestApplicationFactory<T>.TEST_PASSWORD
|
||||
, Account = Environment.GetEnvironmentVariable(nameof(WebTestApplicationFactory<T>.TEST_ACCOUNT)) ??
|
||||
, Account = Environment.GetEnvironmentVariable(nameof(WebTestApplicationFactory<>.TEST_ACCOUNT)) ??
|
||||
WebTestApplicationFactory<T>.TEST_ACCOUNT
|
||||
};
|
||||
var loginAccount = JsonContent.Create(req);
|
||||
|
@ -10,12 +10,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.1",
|
||||
"ace-builds": "1.38.0",
|
||||
"ace-builds": "1.39.0",
|
||||
"aieditor": "1.3.5",
|
||||
"axios": "1.7.9",
|
||||
"axios": "1.8.1",
|
||||
"crypto-js": "4.2.0",
|
||||
"echarts": "5.6.0",
|
||||
"element-plus": "2.9.4",
|
||||
"element-plus": "2.9.5",
|
||||
"json-bigint": "1.0.0",
|
||||
"markdown-it": "14.1.0",
|
||||
"markdown-it-emoji": "3.0.0",
|
||||
@ -23,7 +23,7 @@
|
||||
"sortablejs": "1.15.6",
|
||||
"vkbeautify": "0.99.3",
|
||||
"vue": "3.5.13",
|
||||
"vue-i18n": "11.1.1",
|
||||
"vue-i18n": "11.1.2",
|
||||
"vue-router": "4.5.0",
|
||||
"vue3-ace-editor": "2.2.4",
|
||||
"vue3-json-viewer": "2.2.2",
|
||||
@ -32,11 +32,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
"prettier": "3.5.0",
|
||||
"prettier": "3.5.3",
|
||||
"prettier-plugin-organize-attributes": "1.0.0",
|
||||
"sass": "1.84.0",
|
||||
"terser": "5.38.2",
|
||||
"vite": "6.1.0"
|
||||
"sass": "1.85.1",
|
||||
"terser": "5.39.0",
|
||||
"vite": "6.2.1"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
@ -27,6 +27,17 @@ export default {
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 示例分组计数
|
||||
*/
|
||||
countBy: {
|
||||
url: `${config.API_URL}/api/tpl/example/count.by`,
|
||||
name: `示例分组计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建示例
|
||||
*/
|
||||
|
@ -19,7 +19,7 @@
|
||||
:on-success="success"
|
||||
:show-file-list="showFileList">
|
||||
<slot>
|
||||
<el-button :disabled="disabled" type="primary">Click to upload</el-button>
|
||||
<el-button :disabled="disabled" type="primary">点击上传</el-button>
|
||||
</slot>
|
||||
<template #tip>
|
||||
<div v-if="tip" class="el-upload__tip">{{ tip }}</div>
|
||||
|
@ -134,8 +134,8 @@ export default {
|
||||
//默认值转换为数组
|
||||
toArr(str) {
|
||||
const _arr = []
|
||||
const arr = str.split(',')
|
||||
arr.forEach((item) => {
|
||||
const arr = str?.split(',')
|
||||
arr?.forEach((item) => {
|
||||
if (item) {
|
||||
const urlArr = item.split('/')
|
||||
const fileName = urlArr[urlArr.length - 1]
|
||||
|
@ -10,8 +10,8 @@ export default {
|
||||
parseData: function (res) {
|
||||
return {
|
||||
code: res.code, //分析状态字段结构
|
||||
// fileName: null, //分析文件名称
|
||||
src: res.data, //分析图片远程地址结构
|
||||
fileName: res.data.fileName, //分析文件名称
|
||||
src: res.data.url, //分析图片远程地址结构
|
||||
msg: res.msg, //分析描述字段结构
|
||||
}
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user