首页 > 其他分享 >搭建Wpf框架(17) ——大文件上传与下载

搭建Wpf框架(17) ——大文件上传与下载

时间:2023-09-23 23:57:53浏览次数:44  
标签:return string 17 new var path Wpf 上传

先上效果图:

大文件上传

1.客户端需要按照块拆成一块一块,先计算大小,然后计算块的个数,然后按块逐个上传,代码如下:

public async Task<UploadResult> UploadFileChunck(string path, Action<double> progressAction)
        {
            try
            {
                var fStream = File.OpenRead(path);
                int chunckSize = 2097152;//2MB
                int totalChunks = (int)(fStream.Length / chunckSize);
                if (fStream.Length % chunckSize != 0)
                {
                    totalChunks++;
                }

                double progress = 0d;
                progressAction?.Invoke(progress);

                var tempDirectory = Guid.NewGuid().ToString("N");
                UploadResult result = null;
                for (int i = 0; i < totalChunks; i++)
                {
                    long positon = (i * (long)chunckSize);
                    int toRead = (int)Math.Min(fStream.Length - positon, chunckSize);
                    byte[] buffer = new byte[toRead];
                    await fStream.ReadAsync(buffer, 0, buffer.Length);

                    using (MultipartFormDataContent data = new MultipartFormDataContent())
                    {
                        data.Add(new StringContent(tempDirectory ?? ""), "tempDirectory");
                        data.Add(new StringContent(i.ToString()), "index");
                        data.Add(new StringContent(totalChunks.ToString()), "total");
                        data.Add(new ByteArrayContent(buffer), "file", Path.GetFileName(path));

                        var content = await PostAsync(string.Format("{0}/Base_Manage/Upload/UploadFileChunck", Url), data, TimeOut, Header.GetHeader());
                        result = JsonConvert.DeserializeObject<AjaxResult<UploadResult>>(content)?.Data;

                        progress += 1d / totalChunks;
                        progressAction?.Invoke(progress);
                    }
                }
                fStream.Close();
                return result;
            }
            catch (Exception ex)
            {
                return new UploadResult() { status = ex.Message };
            }
        }

注:加了一个Action用于通知进度 2.服务端需要一块一块接送,直接最后一块接收后,把文件合并,删除块文件,结束,代码如下:

       #region  大文件上传
        /// <summary>
        /// 上传附件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult> UploadFileChunck(IFormFile file, string tempDirectory, int index, int total)
        {
            if (file == null)
            {
                return BadRequest("请选择上传文件");
            }

            string fileUploadPath = GetUploadPath();
         
            string tmp = Path.Combine(fileUploadPath, tempDirectory) + "/";//临时保存分块的目录
            if (index == 0)
            {
                if (Directory.Exists(tmp))
                {
                    Directory.Delete(tmp);
                }
            }
            try
            {
                using (var stream = file.OpenReadStream())
                {
                    var strmd5 = GetMD5Value(stream);
                    //if (md5 == strmd5)//校验MD5值
                    //{
                    //}

                    if (await Save(stream, tmp, index.ToString()))
                    {
                      
                        bool mergeOk = false;
                        string path = "";
                        string physicPath = "";
                        if (total - 1 == index)
                        {
                            path = $"/Upload/{Guid.NewGuid().ToString("N")}/{file.FileName}";
                            physicPath = GetAbsolutePath($"~{path}");
                            string dir = Path.GetDirectoryName(physicPath);
                            if (!Directory.Exists(dir))
                                Directory.CreateDirectory(dir);

                            mergeOk = await FileMerge(tmp, physicPath);
                            if (mergeOk)
                            {
                                _logger.LogInformation($"文件上传成功:{physicPath}");
                            }
                        }

                        string url = $"{AppSettingsConfig.webUrl}{path}";
                        var res = new
                        {
                            index = index,
                            name = file.FileName,
                            status = mergeOk == true ? "done" :"part",
                            thumbUrl = url,
                            url = url
                        };

                        return AjaxResultActionFilter.Success(res);
                    }
                    else
                    {
                        return AjaxResultActionFilter.Error("上传失败");
                    }
                }
            }
            catch (Exception ex)
            {
                Directory.Delete(tmp);//删除文件夹
                _logger.LogError($"文件上传异常:{ex.Message}");
                return AjaxResultActionFilter.Error("上传失败");
            }

        }
        /// <summary>
        /// 合并文件
        /// </summary>
        /// <param name="tmpDirectory">临时上传目录</param>        
        /// <param name="path">上传目录</param>
        /// <param name="saveFileName">保存之后新文件名</param>
        /// <returns></returns>
        private async Task<bool> FileMerge(string tmpDirectory, string saveName)
        {
            try
            {
                var files = Directory.GetFiles(tmpDirectory);

                using (var fs = new FileStream(saveName, FileMode.Create))
                {
                    foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))
                    {
                        var bytes = System.IO.File.ReadAllBytes(part);
                        await fs.WriteAsync(bytes, 0, bytes.Length);
                        bytes = null;
                        System.IO.File.Delete(part);//删除分块
                    }
                    fs.Close();

                    Directory.Delete(tmpDirectory);//删除临时目录
                    return true;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"文件合并异常:{ex.Message}");
                return false;
            }

        }

        #endregion       

        /// <summary>
        /// 文件保存到本地
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="path"></param>
        /// <param name="saveName"></param>
        /// <returns></returns>
        private async Task<bool> Save(Stream stream, string path, string saveName)
        {
            try
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

                await Task.Run(() =>
                {
                    FileStream fs = new FileStream(path + saveName, FileMode.Create);
                    stream.Position = 0;
                    stream.CopyTo(fs);
                    fs.Close();
                });
                return true;

            }
            catch (Exception ex)
            {
                _logger.LogError($"文件保存异常:{ex.Message}");
                return false;
            }
        }


        /// <summary>
        /// 计算文件的MD5值
        /// </summary>
        /// <param name="obj">类型只能为string or stream,否则将会抛出错误</param>
        /// <returns>文件的MD5值</returns>
        private string GetMD5Value(object obj)
        {
            MD5 md5Hash = MD5.Create();
            byte[] data = null;
            switch (obj)
            {
                case string str:
                    data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(str));
                    break;
                case Stream stream:
                    data = md5Hash.ComputeHash(stream);
                    break;
                case null:
                    throw new ArgumentException("参数不能为空");
                default:
                    throw new ArgumentException("参数类型错误");
            }

            return BitConverter.ToString(data).Replace("-", "");
        }

        /// <summary>
        /// 获取路径
        /// </summary>
        /// <param name="virtualPath"></param>
        /// <returns></returns>
        protected string GetAbsolutePath(string virtualPath)
        {
            string path = virtualPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
            if (path[0] == '~')
                path = path.Remove(0, 2);
            string rootPath = HttpContext.RequestServices.GetService<IWebHostEnvironment>().WebRootPath;

            return Path.Combine(rootPath, path);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected string GetUploadPath()
        {
            string rootPath = HttpContext.RequestServices.GetService<IWebHostEnvironment>().WebRootPath;

            return Path.Combine(rootPath, "Upload");
        }

示例实现的比较简单,您还可以进行优化,比如所有块拆分后同时长传,同时上传完毕后,客户在发起合并请求,另外如果丢了一块,其实也是可以进行检查,补上传的。

大文件下载

使用开源框架Downloader,大家自己去GitHub看吧,链接https://github.com/bezzad/Downloader,下图为官网例子图

支持分块同时下载,非常不错哟。

最后老规矩,上源码地址

前台 https://gitee.com/akwkevin/aistudio.-wpf.-aclient

后台 https://gitee.com/akwkevin/AIStudio.Blazor.App

 

标签:return,string,17,new,var,path,Wpf,上传
From: https://www.cnblogs.com/akwkevin/p/17725448.html

相关文章

  • 文件的上传和下载
    文件的上传1.我们先测试打通服务器upload.jsp负责文件的上传<%--CreatedbyIntelliJIDEA.User:SWTDate:2023/9/22Time:18:39TochangethistemplateuseFile|Settings|FileTemplates.--%><%@pagecontentType="text/html;charset=UTF-8"lan......
  • 217. 存在重复元素
    给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。示例1:输入:nums=[1,2,3,1]输出:true示例2:输入:nums=[1,2,3,4]输出:false示例 3:输入:nums=[1,1,1,3,3,4,3,2,4,2]输出:true第一个想到的是用hash表,有冲突......
  • async/await 致WPF卡死问题
    问题代码:xmal:一个按钮+一个显示框 1<ButtonWidth="100"Height="50"Margin="10"Click="Button_Click">test</Button>2<TextBoxx:Name="display"Width="300"Height="300"></Te......
  • 漏洞_MS17-010永恒之蓝
    永恒之蓝简介永恒之蓝是指2017年4月14日晚,黑客团体ShadowBrokers(影子经纪人)公布一大批网络攻击工具,其中包含“永恒之蓝”工具,“永恒之蓝”利用Windows系统的SMB漏洞可以获取系统最高权限。5月12日,不法分子通过改造“永恒之蓝”制作了wannacry勒索病毒,英国、俄罗斯、整个欧洲以及......
  • 大文件上传之--切片上传和断点续传
    <template>  <div id="app">    <el-upload      drag      action      :auto-upload="false"      :show-file-list="false"      :on-change="changeFile"    >      <i class="el-icon-upload"&g......
  • [COCI2016-2017#4] Osmosmjerka 题解
    [COCI2016-2017#4]Osmosmjerka题解我们发现对于每个点,只有八个方向,也就是说,最终能得到的字符串只会有\(8nm\)个,那我们可以考虑把这些字符串的哈希值求出来,相同的哈希值代表选到相同字符串的一种可能,直接统计即可。现在的问题就在于,怎么快速地求出这\(8nm\)个字符串的哈希......
  • qbxt 4179 积木中赛(block)
    原题小P准备了一次预测活动,每个参与活动的人都可以在PPP队获胜,GGG队获胜和平局三种结果中选择自己要预测的一种。如果第\(i\)个人预测正确,那么小P需要付给他\(a_i\)元,否则他需要给小P付\(b_i\)元。小P目前已经收到了\(n\)个人报名参加活动的信息,并知道他们每......
  • 大文件切片上传和断点续传
    大文件分片上传前端知识点md5加密算法用于确保信息传输完整一致sparkmd5在散列大量数据(例如文件)时表现得更好。可以使用FileReader和Blob读取块中的文件,并将每个块附加到md5//创建一个sparkmd5计算arrayBuffer的对象spark=newSparkMD5.ArrayBuffer()//添加到arrayb......
  • Dcoekr 制作 jdk-17 镜像
    Dcoekr制作jdk-17镜像参考文档:jdk17.0.4.1镜像-_ideal-博客园(cnblogs.com)Docker之dockerfile制作jdk镜像-沦陷-博客园(cnblogs.com)((20230307193521-hke61kh'JDK官网下载'))编写DockerfileFROMcentos:7MAINTAINERxxx"[email protected]"WORKDIR/javaxh_......
  • 前端上传大文件处理(切片、断点续传)
    思路1.对文件做切片,即将一个请求拆分成多个请求,每个请求的时间就会缩短,且如果某个请求失败,只需要重新发送这一次请求即可,无需从头开始2.通知服务器合并切片,在上传完切片后,前端通知服务器做合并切片操作3.控制多个请求的并发量,防止多个请求同时发送,造成浏览器内存溢出,导致页......