Explorar o código

merge confict

xf hai 9 horas
pai
achega
1ecd145051

+ 59 - 14
src/FileStorage.Host/Controllers/FileController.cs

@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.StaticFiles;
 using Microsoft.Extensions.Options;
 using SharpCompress.Common;
 using SqlSugar;
+using System.IO;
 using XF.Domain.Authentications;
 
 namespace FileStorage.Host.Controllers
@@ -17,24 +18,44 @@ namespace FileStorage.Host.Controllers
         private readonly ILogger<FileController> _logger;
         private readonly IFileStorage _fileStorage;
         private readonly ISugarUnitOfWork<FileStorageDbContext> _uow;
-
+        private readonly IZipHelper _zipHelper;
         public FileController(
             ILogger<FileController> logger,
             IFileStorage fileStorage,
-            ISugarUnitOfWork<FileStorageDbContext> uow
-            )
+            ISugarUnitOfWork<FileStorageDbContext> uow,
+            IZipHelper zipHelper)
         {
             _logger = logger;
             _fileStorage = fileStorage;
             _uow = uow;
+            _zipHelper = zipHelper;
+        }
+        /// <summary>
+        /// 内部调用,不限制文件格式
+        /// </summary>
+        /// <param name="fileData"></param>
+        /// <param name="source"></param>
+        /// <returns></returns>
+        [HttpPost("uploadinside")]
+        public async Task<IActionResult> UploadInside(IFormFile fileData, string source)
+        { 
+            var file = await _fileStorage.UploadAsync(fileData.FileName, fileData.Length, "", fileData.OpenReadStream(), source ?? string.Empty, false);
+            var Path = await _fileStorage.GetFileUrlIndefinitelyAsync(file.Id, source ?? string.Empty);
+            return Ok(new { Id = file.Id, fileName = file.FileName, Path = Path });
         }
 
+        /// <summary>
+        /// 限制文件格式
+        /// </summary>
+        /// <param name="fileData"></param>
+        /// <param name="source"></param>
+        /// <returns></returns>
         [HttpPost("upload")]
-        public async Task<IActionResult> Upload(IFormFile fileData,string source)
+        public async Task<IActionResult> Upload(IFormFile fileData, string source)
         {
-            var file = await _fileStorage.UploadAsync(fileData.FileName, fileData.Length, "", fileData.OpenReadStream(), source ?? string.Empty);
-            var Path = await  _fileStorage.GetFileUrlIndefinitelyAsync(file.Id, source ?? string.Empty);
-			return Ok(new { Id = file.Id, fileName = file.FileName,Path = Path });
+            var file = await _fileStorage.UploadAsync(fileData.FileName, fileData.Length, "", fileData.OpenReadStream(), source ?? string.Empty, true);
+            var Path = await _fileStorage.GetFileUrlIndefinitelyAsync(file.Id, source ?? string.Empty);
+            return Ok(new { Id = file.Id, fileName = file.FileName, Path = Path });
         }
 
         [HttpGet("getfileurl")]
@@ -48,7 +69,7 @@ namespace FileStorage.Host.Controllers
         [HttpGet("downloadfile")]
         public async Task<IActionResult> DownLoadFile([FromQuery] UploadGetDto dto)
         {
-            var (stream, content, fileName) = _fileStorage.DownLoadFile(dto.Id, dto.Source ?? string.Empty);
+            var (stream, content, fileName) = _fileStorage.DownLoadFile(dto.Id);
             if (stream != null)
             {
                 return File(stream, content, fileName);
@@ -56,6 +77,30 @@ namespace FileStorage.Host.Controllers
             return null;
         }
 
+        /// <summary>
+        /// 批量下载
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("downloadfile_batch")]
+        public async Task<IActionResult> BatchDownLoadFiles([FromBody] BatchDownLoadFilesDtos dto)
+        {
+            var streams = new Dictionary<string, Stream>();
+            foreach (var Id in dto.Ids)
+            {
+                var (stream, content, fileName) = _fileStorage.DownLoadFile(Id);
+                streams.Add(fileName, stream);
+            }
+
+            //调用压缩方法 进行压缩 (接收byte[] 数据)
+            byte[] fileBytes = _zipHelper.ConvertZipStream(streams);
+            HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
+            var name = DateTime.Now.ToString("yyyyMMddHHmmss");
+            return File(fileBytes, "application/octet-stream", $"{name}.zip");
+            //return null;
+        }
+
+
         [HttpGet("delfile")]
         public async Task<bool> DelFile([FromQuery] UploadGetDto dto)
         {
@@ -65,11 +110,11 @@ namespace FileStorage.Host.Controllers
         [HttpGet("file_path")]
         public async Task<IActionResult> GetFilePath([FromQuery] UploadGetDto dto)
         {
-	        var uri = await _fileStorage.GetFilePathAsync(dto.Id, dto.Source ?? string.Empty);
-	        return Ok(new { Path = uri });
+            var uri = await _fileStorage.GetFilePathAsync(dto.Id, dto.Source ?? string.Empty);
+            return Ok(new { Path = uri });
         }
 
-		[HttpGet("createdb")]
+        [HttpGet("createdb")]
         public Task CreateDb()
         {
             var db = _uow.Db;
@@ -88,9 +133,9 @@ namespace FileStorage.Host.Controllers
         /// <returns></returns>
         [AllowAnonymous]
         [HttpGet("files")]
-        public async Task Files(string id,string expires,string clientid,string signature)
+        public async Task Files(string id, string expires, string clientid, string signature)
         {
-            var fullPath = await _fileStorage.GetFilePath(id,expires,clientid,signature);
+            var fullPath = await _fileStorage.GetFilePath(id, expires, clientid, signature);
             await HttpContext.Response.SendFileAsync(fullPath);
         }
 
@@ -137,5 +182,5 @@ namespace FileStorage.Host.Controllers
         {
             return DateTime.Now.ToString();
         }
-	}
+    }
 }

+ 2 - 0
src/FileStorage.Host/StartupExtensions.cs

@@ -23,6 +23,8 @@ internal static class StartupExtensions
 #endif
 
         services.Configure<StorageConfiguration>(d => configuration.GetSection(nameof(StorageConfiguration)).Bind(d));
+
+        services.Configure<FileExtConfiguration>(d => configuration.GetSection(nameof(FileExtConfiguration)).Bind(d));
         // Add services to the container.
         services.BatchInjectServices();
 

+ 3 - 0
src/FileStorage.Host/config/appsettings.Development.json

@@ -25,5 +25,8 @@
     "Ctyun": {
 
     }
+  },
+  "FileExtConfiguration": {
+     "FileExt": "doc,docx,pdf,xls,xlsx,jpg,png,mpg,mp4,mov,mp3,txt"
   }
 }

+ 3 - 0
src/FileStorage.Host/config/appsettings.json

@@ -28,5 +28,8 @@
   },
   "Ids4AuthorizeOptions": {
     "Authority": "http://110.188.24.28:50200"
+  },
+  "FileExtConfiguration": {
+    "FileExt": "doc,docx,pdf,xls,xlsx,jpg,png,mpg,mp4,mov,mp3,txt"
   }
 }

+ 66 - 44
src/FileStorage/DefaultFileStorage.cs

@@ -1,5 +1,6 @@
 using FileStorage.Extensions;
 using Microsoft.AspNetCore.StaticFiles;
+using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
@@ -11,11 +12,15 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
 
     private readonly IFileMetadataRepository _fileMetadataRepository;
     private readonly IOptionsSnapshot<StorageConfiguration> _fileUploadOptions;
+    private readonly IOptionsSnapshot<FileExtConfiguration> _fileExtOptions;
+    private readonly ILogger<DefaultFileStorage> _logger;
 
-    public DefaultFileStorage(IFileMetadataRepository fileMetadataRepository,IOptionsSnapshot<StorageConfiguration> optionsSnapshot)
+    public DefaultFileStorage(IFileMetadataRepository fileMetadataRepository, IOptionsSnapshot<StorageConfiguration> optionsSnapshot, ILogger<DefaultFileStorage> logger, IOptionsSnapshot<FileExtConfiguration> fileExtOptions)
     {
         _fileMetadataRepository = fileMetadataRepository;
         _fileUploadOptions = optionsSnapshot;
+        _logger = logger;
+        _fileExtOptions = fileExtOptions;
     }
 
     /// <summary>
@@ -26,8 +31,9 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
     /// <param name="extraInfo"></param>
     /// <param name="fileData"></param>
     /// <param name="client"></param>
+    /// <param name="isVerification">是否验证文件格式</param>
     /// <returns></returns>
-    public async Task<FileMetadata> UploadAsync(string fileName, long length, string extraInfo, Stream fileData, string client)
+    public async Task<FileMetadata> UploadAsync(string fileName, long length, string extraInfo, Stream fileData, string client, bool? isVerification)
     {
         if (fileName == null)
         {
@@ -35,7 +41,7 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
         }
         fileName = fileName.Replace("<", "").Replace(">", "").Replace(" ", "");
 
-        var rv = UploadLoca(fileName, length, extraInfo, fileData,client);
+        var rv = UploadLoca(fileName, length, extraInfo, fileData, client, isVerification);
         if (!string.IsNullOrEmpty(rv))
         {
             FileMetadata fileModel = new FileMetadata();
@@ -59,9 +65,9 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
         return null;
     }
 
-    public async Task<Uri> GetFileUrlAsync(string id,string clientId)
+    public async Task<Uri> GetFileUrlAsync(string id, string clientId)
     {
-        var fileMetadata = await _fileMetadataRepository.GetAsync(x=>x.Id== id && x.Client== clientId);
+        var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
         if (fileMetadata != null)
         {
             long sTime = (long)(DateTime.Now.AddHours(1).ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
@@ -75,34 +81,34 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
 
     public async Task<Uri> GetFileUrlIndefinitelyAsync(string id, string clientId)
     {
-	    var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
-	    if (fileMetadata != null)
-	    {
-		    long sTime = (long)(DateTime.Now.AddHours(1).ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
-		    string encryptText = clientId + "|" + sTime + "|" + id;
-		    var signatureText = DESExtensions.Encrypt(encryptText);
-		    return new Uri("/file/files_indefinitely?id=" + id + "&expires=" + sTime + "&clientid=" + clientId + "&signature=" + signatureText);
-	    }
-	    throw new UserFriendlyException("无权限访问");
+        var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
+        if (fileMetadata != null)
+        {
+            long sTime = (long)(DateTime.Now.AddHours(1).ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
+            string encryptText = clientId + "|" + sTime + "|" + id;
+            var signatureText = DESExtensions.Encrypt(encryptText);
+            return new Uri("/file/files_indefinitely?id=" + id + "&expires=" + sTime + "&clientid=" + clientId + "&signature=" + signatureText);
+        }
+        throw new UserFriendlyException("无权限访问");
     }
 
-	public (Stream stream, string contentType,string fileName) DownLoadFile(string id,string clientId)
+    public (Stream stream, string contentType, string fileName) DownLoadFile(string id)
     {
-        var fileMetadata = _fileMetadataRepository.Get(x=>x.Id == id && x.Client == clientId);
-        if (fileMetadata!=null)
+        var fileMetadata = _fileMetadataRepository.Get(x => x.Id == id);
+        if (fileMetadata != null)
         {
             string filePath = Path.Combine(Directory.GetCurrentDirectory(), fileMetadata.Path);
-            var contentType =  TaskGetFileContentTypeAsync(fileMetadata.Path);
+            var contentType = TaskGetFileContentTypeAsync(fileMetadata.Path);
             var stream = File.OpenRead(filePath);
-            return (stream,contentType,fileMetadata.FileName);
+            return (stream, contentType, fileMetadata.FileName);
         }
         throw new UserFriendlyException("无权限访问");
     }
 
     public async Task<bool> DelFileAsync(string id, string clientId)
     {
-        var fileMetadata = await _fileMetadataRepository.GetAsync(x=>x.Id == id && x.Client == clientId);
-        if (fileMetadata!=null)
+        var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
+        if (fileMetadata != null)
         {
             string filePath = Path.Combine(Directory.GetCurrentDirectory(), fileMetadata.Path);
             await _fileMetadataRepository.RemoveAsync(id);
@@ -114,17 +120,17 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
 
     public async Task<string> GetFilePathAsync(string id, string clientId)
     {
-	    var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
-	    return fileMetadata?.Path!;
+        var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
+        return fileMetadata?.Path!;
     }
 
-	public async Task<string> GetFilePath(string id, string expires, string clientId, string signature)
+    public async Task<string> GetFilePath(string id, string expires, string clientId, string signature)
     {
         string decryptText = DESExtensions.Decrypt(signature);
         if (!string.IsNullOrEmpty(decryptText))
         {
             string[] paramData = decryptText.Split('|');
-            
+
             if (id != paramData[2] || clientId != paramData[0] || expires != paramData[1])
                 throw UserFriendlyException.SameMessage("参数不合法");
 
@@ -141,27 +147,27 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
     }
 
 
-	public async Task<string> GetFilePathIndefinitely(string id, string expires, string clientId, string signature)
-	{
-		string decryptText = DESExtensions.Decrypt(signature);
-		if (!string.IsNullOrEmpty(decryptText))
-		{
-			string[] paramData = decryptText.Split('|');
+    public async Task<string> GetFilePathIndefinitely(string id, string expires, string clientId, string signature)
+    {
+        string decryptText = DESExtensions.Decrypt(signature);
+        if (!string.IsNullOrEmpty(decryptText))
+        {
+            string[] paramData = decryptText.Split('|');
 
-			if (id != paramData[2] || clientId != paramData[0] || expires != paramData[1])
-				throw UserFriendlyException.SameMessage("参数不合法");
+            if (id != paramData[2] || clientId != paramData[0] || expires != paramData[1])
+                throw UserFriendlyException.SameMessage("参数不合法");
 
-			var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
-			if (fileMetadata == null)
-				throw UserFriendlyException.SameMessage("无权限访问");
+            var fileMetadata = await _fileMetadataRepository.GetAsync(x => x.Id == id && x.Client == clientId);
+            if (fileMetadata == null)
+                throw UserFriendlyException.SameMessage("无权限访问");
 
-			return GetFullPath(fileMetadata.Path);
-		}
-		throw UserFriendlyException.SameMessage("无权限访问");
-	}
+            return GetFullPath(fileMetadata.Path);
+        }
+        throw UserFriendlyException.SameMessage("无权限访问");
+    }
 
 
-	private string TaskGetFileContentTypeAsync(string fileName)
+    private string TaskGetFileContentTypeAsync(string fileName)
     {
         string suffix = Path.GetExtension(fileName);
         var provider = new FileExtensionContentTypeProvider();
@@ -170,7 +176,7 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
     }
 
 
-    private string UploadLoca(string fileName, long length, string extraInfo, Stream fileData,string client)
+    private string UploadLoca(string fileName, long length, string extraInfo, Stream fileData, string client, bool? isVerification)
     {
         var settings = _fileUploadOptions.Value.Local;
         string groupDir = "";
@@ -184,22 +190,38 @@ public class DefaultFileStorage : IFileStorage, IScopeDependency
         }
         string sub = DateTime.Now.ToString("yyyyMMdd");
 
-        groupDir = Path.Combine(groupDir,client, sub);
+        groupDir = Path.Combine(groupDir, client, sub);
 
         string pathHeader = groupDir;
 
         string fulldir = GetFullPath(pathHeader);
+
+        _logger.LogInformation("路径:"+fulldir);
         if (!Directory.Exists(fulldir))
         {
             Directory.CreateDirectory(fulldir);
         }
-
+        _logger.LogInformation("已执行");
         var ext = string.Empty;
         if (!string.IsNullOrEmpty(fileName))
         {
             var dotPos = fileName.LastIndexOf('.');
+            if (dotPos == 0)
+            {
+                throw UserFriendlyException.SameMessage("文件没有格式,请重新上传文件");
+            }
             ext = fileName.Substring(dotPos + 1);
         }
+        if (isVerification.HasValue && isVerification == true)
+        {
+            //新
+            var extList = _fileExtOptions.Value.FileExt.Split(",").ToList();
+            //extList.Add("m4a");
+            if (!extList.Contains(ext.ToLower()))
+            {
+                throw UserFriendlyException.SameMessage($"文件格式不正确,只能上传【{_fileExtOptions.Value.FileExt}】格式文件");
+            }
+        }
         var filename = $"{Guid.NewGuid().ToString().Replace("-", string.Empty)}.{ext}";
         var fullPath = Path.Combine(fulldir, filename);
         using (var fileStream = File.Create(fullPath))

+ 13 - 0
src/FileStorage/FileExtConfiguration.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FileStorage
+{
+    public class FileExtConfiguration
+    {
+        public string FileExt { get; set; }
+    }
+}

+ 1 - 0
src/FileStorage/FileStorage.csproj

@@ -10,6 +10,7 @@
     <PackageReference Include="Mapster" Version="7.3.0" />
     <PackageReference Include="MediatR" Version="12.0.1" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
+    <PackageReference Include="SharpZipLib" Version="1.4.2" />
     <PackageReference Include="XF.Domain.Repository" Version="1.0.4" />
   </ItemGroup>
 

+ 12 - 2
src/FileStorage/IFileStorage.cs

@@ -3,11 +3,21 @@
     public interface IFileStorage
     {
 
-	    Task<FileMetadata> UploadAsync(string fileName, long length, string extraInfo, Stream fileData, string client);
+        /// <summary>
+        /// 本地上传
+        /// </summary>
+        /// <param name="fileName"></param>
+        /// <param name="length"></param>
+        /// <param name="extraInfo"></param>
+        /// <param name="fileData"></param>
+        /// <param name="client"></param>
+        /// <param name="isVerification">是否验证文件格式</param>
+        /// <returns></returns>
+        Task<FileMetadata> UploadAsync(string fileName, long length, string extraInfo, Stream fileData, string client, bool? isVerification);
 
         Task<Uri> GetFileUrlAsync(string id, string clientId);
 
-        (Stream stream, string contentType,string fileName) DownLoadFile(string id, string clientId);
+        (Stream stream, string contentType,string fileName) DownLoadFile(string id);
 
         Task<bool> DelFileAsync(string id, string clientId);
 

+ 18 - 0
src/FileStorage/IZipHelper.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FileStorage
+{
+    public interface IZipHelper
+    {
+        /// <summary>
+        /// ZipStream 压缩
+        /// </summary>
+        /// <param name="streams">Dictionary(string, Stream) 文件名和Stream</param>
+        /// <returns></returns>
+        byte[] ConvertZipStream(Dictionary<string, Stream> streams);
+    }
+}

+ 7 - 0
src/FileStorage/StorageConfiguration.cs

@@ -52,4 +52,11 @@ namespace FileStorage
 	    public string Id { get; set; }
     }
 
+
+    public class BatchDownLoadFilesDtos
+    {
+        public string Source { get; set; }
+
+        public List<string> Ids { get; set; }
+    }
 }

+ 65 - 0
src/FileStorage/ZipHelper.cs

@@ -0,0 +1,65 @@
+using ICSharpCode.SharpZipLib.Zip;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+
+namespace FileStorage
+{
+    public class ZipHelper : IZipHelper, IScopeDependency
+    {
+        /// <summary>
+        /// ZipStream 压缩
+        /// </summary>
+        /// <param name="streams">Dictionary(string, Stream) 文件名和Stream</param>
+        /// <returns></returns>
+        public byte[] ConvertZipStream(Dictionary<string, Stream> streams)
+        {
+            byte[] buffer = new byte[6500];
+            MemoryStream returnStream = new();
+            var zipMs = new MemoryStream();
+            using (ZipOutputStream zipStream = new ZipOutputStream(zipMs))
+            {
+                zipStream.SetLevel(9);//设置 压缩等级 (9级 500KB 压缩成了96KB)
+                foreach (var kv in streams)
+                {
+                    string fileName = kv.Key;
+                    using (var streamInput = kv.Value)
+                    {
+                        ZipEntry zipEntry = new ZipEntry(fileName);
+                        zipEntry.IsUnicodeText = true;
+                        zipStream.PutNextEntry(zipEntry);
+
+                        while (true)
+                        {
+                            var readCount = streamInput.Read(buffer, 0, buffer.Length);
+                            if (readCount > 0)
+                            {
+                                zipStream.Write(buffer, 0, readCount);
+                            }
+                            else
+                            {
+                                break;
+                            }
+                        }
+                        zipStream.Flush();
+                    }
+                }
+                zipStream.Finish();
+                zipMs.Position = 0;
+                zipMs.CopyTo(returnStream, 5600);
+            }
+            returnStream.Position = 0;
+
+            //Stream转Byte[]
+            byte[] returnBytes = new byte[returnStream.Length];
+            returnStream.Read(returnBytes, 0, returnBytes.Length);
+            returnStream.Seek(0, SeekOrigin.Begin);
+
+            return returnBytes;
+            return null;
+        }
+    }
+}