.NET6 WebAPI文件上传

以下内容展示的是文件分片上传

1.接口项目:

        1.1 控制器

[Route("api/[controller]/[action]")]
    [ApiController]
    public class UploadFilesController : ControllerBase
    {
        [HttpPost]
        [DisableRequestSizeLimit]
        public async Task<UploadResponse> Upload()
        {
            var response = new UploadResponse()
            {
                code = (int)HttpStatusCode.InternalServerError,
                msg = ""
            };
            //判断请求是否为Multipart-FormData类型
            if (!FormDataHelper.IsMultipartContentType(Request.ContentType))
            {
                response = new UploadResponse()
                {
                    code = (int)HttpStatusCode.UnsupportedMediaType,
                    msg = "不支持的请求类型"
                };
            }
            else
            {
                try
                {
                    //配置:可用的磁盘
                    string saveDisk = AppSettingsUtils.UploadFilesConfigs.ContainsKey("SaveDisk") ? AppSettingsUtils.UploadFilesConfigs["SaveDisk"] : string.Empty;
                    //配置:基础地址,用于拼接返回的url
                    string IPHostBase = AppSettingsUtils.UploadFilesConfigs.ContainsKey("IPHostBase") ? AppSettingsUtils.UploadFilesConfigs["IPHostBase"] : string.Empty;
                    //配置:可用磁盘所需最小空间要求
                    string diskThreshold = AppSettingsUtils.UploadFilesConfigs.ContainsKey("DiskThreshold") ? AppSettingsUtils.UploadFilesConfigs["DiskThreshold"] : string.Empty;
                    //配置:基础文件路径
                    string dirBase = AppSettingsUtils.UploadFilesConfigs.ContainsKey("DirBase") ? AppSettingsUtils.UploadFilesConfigs["DirBase"] : string.Empty;
                    if (string.IsNullOrEmpty(saveDisk) || string.IsNullOrEmpty(diskThreshold) || string.IsNullOrEmpty(IPHostBase) || string.IsNullOrEmpty(dirBase))
                    {
                        response = new UploadResponse()
                        {
                            code = (int)HttpStatusCode.BadGateway,
                            msg = "接口未配置相关信息"
                        };
                    }
                    else
                    {
                        //磁盘空间检查
                        double diskLastLength = float.Parse(diskThreshold) * 1024 * 1024 * 1024;
                        string freeDisk = string.Empty;
                        DiskInfoDto diskObj = DiskUtils.DiskCurrentStatus();
                        diskObj.DiskList.ForEach(o =>
                        {
                            if (saveDisk.Contains(o.DiskName.Substring(0, 1)) && float.Parse(o.DiskTotalFreeSize) > diskLastLength)
                            {
                                freeDisk = o.DiskName;
                            }
                        });
                        if (string.IsNullOrEmpty(freeDisk))
                        {
                            response = new UploadResponse()
                            {
                                code = (int)HttpStatusCode.BadGateway,
                                msg = "服务器磁盘空间不足"
                            };
                        }
                        else
                        {
                            //设置文件目录与缓存目录
                            string dirPath = Path.Combine(freeDisk, dirBase);
                            if (!Directory.Exists(dirPath))
                            {
                                Directory.CreateDirectory(dirPath);
                            }
                            string cacheDir = Path.Combine(dirPath, "FileCache");
                            if (!Directory.Exists(cacheDir))
                            {
                                Directory.CreateDirectory(cacheDir);
                            }
                            //获取文件参数
                            StringValues fileName = new StringValues();
                            Request.Form.TryGetValue("FileName", out fileName);
                            StringValues partNumber = new StringValues();
                            Request.Form.TryGetValue("PartNumber", out partNumber);
                            StringValues chunks = new StringValues();
                            Request.Form.TryGetValue("Chunks", out chunks);
                            // 文件分片名
                            var filePartName = fileName + ".partNumber-" + partNumber;
                            //开始读取请求里的文件并写到缓存目录
                            var file = Request.Form.Files.GetFile("file");
                            var path = Path.Combine(cacheDir, filePartName);
                            using (var stream = new FileStream(path, FileMode.Append))
                            {
                                file.CopyTo(stream);
                            }
                            //如果是最后分片,执行合并文件(可能涉及转码等)
                            if (partNumber == chunks)
                            {
                                await FormDataHelper.MergeChunkFile(fileName, dirPath);
                                response = new UploadResponse()
                                {
                                    code = (int)HttpStatusCode.OK,
                                    msg = "上传成功"
                                };
                                response.data = new UploadInner()
                                {
                                    localPath = Path.Combine(dirPath, fileName),
                                    url = IPHostBase + "/" + dirBase + "/" + fileName
                                };
                            }
                            else
                            {
                                response = new UploadResponse()
                                {
                                    code = (int)HttpStatusCode.Accepted,
                                    msg = "已接收分片文件" + partNumber
                                };
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    response = new UploadResponse()
                    {
                        code = (int)HttpStatusCode.InternalServerError,
                        msg = "内部错误:" + ex.Message
                    };
                }
            }
            return response;
        }
    }

        1.2 AppSettingsUtils.cs(如何加载appsettings.json参考我的另一篇博文)

    public class AppSettingsUtils
    {
        private static IConfiguration configuration = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory).AddJsonFile("appsettings.json").Build();


        private static Dictionary<string, string> _UploadFilesConfigs = null;
        public static Dictionary<string, string> UploadFilesConfigs
        {
            get
            {
                if (_UploadFilesConfigs == null)
                {
                    _UploadFilesConfigs = new Dictionary<string, string>();
                    _UploadFilesConfigs.Add("IPHostBase", configuration.GetSection("UploadFilesConfigs:IPHostBase").Value);
                    _UploadFilesConfigs.Add("SaveDisk", configuration.GetSection("UploadFilesConfigs:SaveDisk").Value);
                    _UploadFilesConfigs.Add("DiskThreshold", configuration.GetSection("UploadFilesConfigs:DiskThreshold").Value);
                    _UploadFilesConfigs.Add("DirBase", configuration.GetSection("UploadFilesConfigs:DirBase").Value);
                }
                return _UploadFilesConfigs;
            }
        }
    }

         1.3 DiskUtils.cs

public class DiskUtils
    {
        /// <summary>
        /// 获取硬盘信息
        /// </summary>
        /// <returns></returns>
        private static List<DiskInfo> GetDiskInfo()
        {
            var list = new List<DiskInfo>();
            //获取计算机上的所有逻辑驱动器信息
            var allDrivers = DriveInfo.GetDrives();
            foreach (var info in allDrivers)
            {
                if (!info.IsReady)//磁盘是否准备就绪
                    continue;
                list.Add(new DiskInfo() { DiskName = info.Name, DiskType = info.DriveType.ToString(), DiskTotalSize = info.TotalSize.ToString(), DiskTotalFreeSize = info.TotalFreeSpace.ToString(), DiskFormat = info.DriveFormat });
            }
            return list;
        }
        public static DiskInfoDto DiskCurrentStatus()
        {
            var diskInfo = new DiskInfoDto();

            var diskListInfo = GetDiskInfo();
            diskInfo.DiskName = string.Join(",", diskListInfo.Select(d => d.DiskName.Replace(@"\", "")));
            diskInfo.DiskTotalSize = Math.Round(diskListInfo.Sum(d => float.Parse(d.DiskTotalSize)) / 1024 / 1024 / 1024.0, 2);
            diskInfo.DiskAvailableSize = Math.Round(diskListInfo.Sum(d => float.Parse(d.DiskTotalFreeSize)) / 1024 / 1024 / 1024.0, 2);
            diskInfo.ResidualRate = Math.Round(diskInfo.DiskAvailableSize / diskInfo.DiskTotalSize, 2) * 100;
            diskInfo.DiskList = diskListInfo;
            return diskInfo;
        }
    }

    public class DiskInfoDto
    {
        /// <summary>
        /// 磁盘名称
        /// </summary>
        public string DiskName { get; set; }
        /// <summary>
        /// 磁盘大小
        /// </summary>
        public double DiskTotalSize { get; set; }
        /// <summary>
        /// 剩余空间大小
        /// </summary>
        public double DiskAvailableSize { get; set; }
        /// <summary>
        /// 空闲百分比
        /// </summary>
        public double ResidualRate { get; set; }

        public List<DiskInfo> DiskList { get; set; }
    }

    public class DiskInfo
    {
        /// <summary>
        /// 磁盘盘符名称
        /// </summary>
        public string DiskName { get; set; }
        /// <summary>
        /// 磁盘类型
        /// </summary>
        public string DiskType { get; set; }
        /// <summary>
        /// 磁盘文件系统格式
        /// </summary>
        public string DiskFormat { get; set; }
        /// <summary>
        /// 磁盘空间大小
        /// </summary>
        public string DiskTotalSize { get; set; }
        /// <summary>
        /// 可用空间大小
        /// </summary>
        public string DiskTotalFreeSize { get; set; }
    }

        1.4 FormDataHelper.cs 

public class FormDataHelper
    {
        public static bool IsMultipartContentType(string contentType)
        {
            return
                !string.IsNullOrEmpty(contentType) &&
                contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
        }

        public static async Task MergeChunkFile(string fileName, string targetPath)
        {
            //文件上传目录名
            var uploadDirectoryName = Path.Combine(targetPath, "FileCache");

            //分片文件命名约定
            var partToken = FileSort.PART_NUMBER;

            //上传文件实际名称
            var baseFileName = fileName;

            //根据命名约定查询指定目录下符合条件的所有分片文件
            var searchpattern = $"{Path.GetFileName(baseFileName)}{partToken}*";

            //获取所有分片文件列表
            var filesList = Directory.GetFiles(uploadDirectoryName, searchpattern);
            if (!filesList.Any()) { return; }

            var mergeFiles = new List<FileSort>();

            foreach (string file in filesList)
            {
                var fileSize = new FileInfo(file).Length;
                var sort = new FileSort
                {
                    FileName = file
                };

                var fileIndex = file.Substring(file.IndexOf(partToken) + partToken.Length);

                int.TryParse(fileIndex, out var number);
                if (number <= 0) { continue; }

                sort.PartNumber = number;

                mergeFiles.Add(sort);
            }

            // 按照分片排序
            var mergeOrders = mergeFiles.OrderBy(s => s.PartNumber).ToList();

            // 合并文件
            using var fileStream = new FileStream(Path.Combine(targetPath, baseFileName), FileMode.Create);
            foreach (var fileSort in mergeOrders)
            {
                using FileStream fileChunk = new FileStream(fileSort.FileName, FileMode.Open);
                await fileChunk.CopyToAsync(fileStream);
            }

            //删除分片文件
            DeleteFile(mergeFiles);

        }

        private static void DeleteFile(List<FileSort> files)
        {
            foreach (var file in files)
            {
                try
                {
                    System.IO.File.Delete(file.FileName);
                }
                catch { }
            }
        }
    }

    public class FileSort
    {
        public const string PART_NUMBER = ".partNumber-";
        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName { get; set; }
        /// <summary>
        /// 文件分片号
        /// </summary>
        public int PartNumber { get; set; }
    }

        1.5 appsetting.json

{
"UploadFilesConfigs": {
    "IPHostBase": "http://xx.xx.xx.xx:端口号",
    "SaveDisk": "C|D|E|F",
    "DiskThreshold": "20",//单位GB
    "DirBase": "TestDir"
  }
}

 

2.客户端调用:

        2.1 方法

public static bool UploadFile(string url, string filePath, ref string networkPath, ref string localPath, ref string msg)
        {
            using var httpClient = new HttpClient();
            using var fileStream = File.OpenRead(filePath);

            // 文件名
            string fileName = Path.GetFileName(filePath);
            var fileChunks = new List<FileChunk>();
            // 设置分片缓冲区大小KB
            var maxFileSizeMB = 300;
            var bufferChunkSize = maxFileSizeMB * 1024;
            // 文件大小
            var total = fileStream.Length;
            int partNumber = 1;
            long readed = 0;// 已读大小
            // 文件分片
            while (readed < total)
            {
                if (total - readed < bufferChunkSize)
                {
                    int last = (int)(total - readed);
                    FileChunk chunk = new FileChunk()
                    {
                        FileName = fileName,
                        PartNumber = partNumber,
                        PartSize = last,
                    };
                    chunk.PartFileBytes = new byte[(int)(last)];
                    int readbytes = fileStream.Read(chunk.PartFileBytes, 0, last);
                    fileChunks.Add(chunk);
                    readed += readbytes;
                }
                else
                {
                    FileChunk chunk = new FileChunk()
                    {
                        FileName = fileName,
                        PartNumber = partNumber,
                        PartSize = bufferChunkSize,
                    };
                    chunk.PartFileBytes = new byte[bufferChunkSize];
                    int readbytes = fileStream.Read(chunk.PartFileBytes, 0, bufferChunkSize);
                    fileChunks.Add(chunk);
                    readed += readbytes;
                }
                partNumber += 1;
            }
            string str = string.Empty;
            for (int i = 0; i < fileChunks.Count; i++)
            {
                //参数
                using var formData = new MultipartFormDataContent();
                formData.Add(new StringContent(fileName), "FileName");
                formData.Add(new StringContent(fileChunks[i].PartNumber.ToString()), "PartNumber");
                formData.Add(new StringContent(fileChunks.Count.ToString()), "Chunks");
                formData.Add(new ByteArrayContent(fileChunks[i].PartFileBytes), "file", fileChunks[i].FileName + ".partNumber-" + fileChunks[i].PartNumber);

                var response = httpClient.PostAsync(url, formData).Result;
                if (!response.IsSuccessStatusCode)
                {
                    msg = "文件上传意外中断";
                    break;
                }
                else
                {
                    var res = response.Content.ReadFromJsonAsync<Request.UploadResponse>().Result;
                    if (res != null && res.code == 200)
                    {
                        //所有分片传输完毕
                        networkPath = res.data.url;
                        localPath = res.data.localPath;
                        msg = res.msg;
                        return true;
                    }
                    else if (res != null && res.code == 202)
                    {
                        //分片传输成功,循环继续
                    }
                    else
                    {
                        msg = res != null ? res.msg : "文件上传失败";
                        return false;
                    }
                }
            }
            return false;
        }

         2.2 FileChunk.cs

public class FileChunk
        {
            /// <summary>
            /// 文件名
            /// </summary>
            public string FileName { get; set; }
            /// <summary>
            /// 当前分片
            /// </summary>
            public int PartNumber { get; set; }
            /// <summary>
            /// 缓冲区大小
            /// </summary>
            public int PartSize { get; set; }
            /// <summary>
            /// 分片文件
            /// </summary>
            public byte[] PartFileBytes { get; set; }
        }

总结:复制粘贴

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值