IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> .NET Core WebApi中文件分片下载 -> 正文阅读

[JavaScript知识库].NET Core WebApi中文件分片下载

一、实现效果

.NET Core WebApi基础入门项目源码下载

二、实现效果

2.1、文件分片下载思路

2.2、文件分片下载的后端实现?

①首先定义文件下载请求、分段下载的接口且编写

/***
*	Title:".NET Core WebApi" 项目
*		主题:文件控制
*	Description:
*		功能:
*		    2-下载文件流程:
*		        ①接受前端上传文件的预览信息【文件大小、
*		           分片数量、文件的md5值、文件扩展类型、文件名称】请求;
*		           生成该请求的guid,存在缓存中,返回对应消息。
*		        ②根据获取到的文件分片数量循环请求文件分片数据进行下载
*		          【也可单独请求下载某一文件片段流数据】
*	Date:2021
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
*/

using log4net;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Utils;
using WebApiUtils;

namespace WebApi_Learn.Controllers
{

    [Route("api/[controller]")]
    [ApiController]
    public class FileController : ControllerBase
    {
        #region   基础参数
        //日志
        private readonly ILog _log = LogManager.GetLogger(WebApi_Learn.Startup.repository.Name, typeof(FileController));

        #endregion



        #region   公有方法

        /// <summary>
        /// 请求下载文件
        /// </summary>
        /// <param name="fileInfo">文件参数信息[name]</param>
        /// <returns></returns>
        [HttpPost, Route("RequestDownload")]
        public MessageEntity RequestDownloadFile([FromBody] Dictionary<string, object> fileInfo)
        {
            //string path = $".{GetFilePath(AppConfigHelper.GetUploadFileBasePath)}";
            //DirectoryInfo root = new DirectoryInfo(path);
            //FileInfo[] files = root.GetFiles();
            //foreach (var item in files)
            //{
            //    _log.Debug($"{path} 目录下的文件为:"+item.Name);
            //}
            return RequestDownloadFile(fileInfo,AppConfigHelper.GetUploadFileBasePath, _log);
        }


        /// <summary>
        /// 分段下载文件
        /// </summary>
        /// <param name="fileInfo">请求参数信息[index,name]</param>
        /// <returns></returns>
        [HttpPost, Route("Download")]
        public async Task<IActionResult> FileDownload([FromBody] Dictionary<string, object> fileInfo)
        {
           return await FileDownload(fileInfo,AppConfigHelper.GetUploadFileBasePath, _log);
        }

        #endregion 



        #region   私有方法

        /// <summary>
        /// 请求下载文件
        /// </summary>
        /// <param name="fileInfo">文件参数信息[name]</param>
        /// <param name="fileBasePath">下载文件的基础路径</param>
        /// <param name="log">日志</param>
        /// <returns>返回消息实体</returns>
        private MessageEntity RequestDownloadFile([FromBody] Dictionary<string, object> fileInfo, string fileBasePath, ILog log)
        {
            MessageEntity message = new MessageEntity();
            string fileName = string.Empty;
            string fileExt = string.Empty;
            if (fileInfo.ContainsKey("name"))
            {
                fileName = fileInfo["name"].ToString();
            }
            if (fileInfo.ContainsKey("ext"))
            {
                fileExt = fileInfo["ext"].ToString();
            }
            if (string.IsNullOrEmpty(fileName))
            {
                message.Code = -1;
                message.Msg = "文件名不能为空";
                return message;
            }
            //获取对应目录下文件,如果有,获取文件开始准备分段下载
            string filePath = GetFilePathAndName(fileBasePath, fileName);
            filePath = $"{filePath}{fileExt}";
            log.Debug($"下载文件的路径:{filePath}");
            FileStream fs = null;
            try
            {
                if (!System.IO.File.Exists(filePath))
                {
                    //文件为空
                    message.Code = -1;
                    message.Msg = $"请求的:{filePath} 文件不存在!";
                    return message;
                }
                fs = new FileStream(filePath, FileMode.Open);
                if (fs.Length <= 0)
                {
                    //文件为空
                    message.Code = -1;
                    message.Msg = "文件尚未处理完";
                    return message;
                }
                int shardSize = 1 * 1024 * 1024;//一次1M
                RequestFileUploadEntity request = new RequestFileUploadEntity();
                request.fileext = fileExt;
                request.size = fs.Length;
                request.count = (int)(fs.Length / shardSize);
                if ((fs.Length % shardSize) > 0)
                {
                    request.count += 1;
                }
                request.filedata = GetCryptoString(fs);

                message.Data = request;
            }
            catch (Exception ex)
            {
                log.Debug($"读取文件信息失败:{filePath},错误信息:{ex.Message}");
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                }
            }

            return message;
        }


        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="fileInfo">文件信息</param>
        /// <param name="fileBasePath">下载文件的基础路径</param>
        /// <param name="log">日志</param>
        /// <returns></returns>
        private async Task<IActionResult> FileDownload([FromBody] Dictionary<string, object> fileInfo, string fileBasePath, ILog log)
        {
            //开始根据片段来下载
            int index = 0;
            if (fileInfo.ContainsKey("index"))
            {
                int.TryParse(fileInfo["index"].ToString(), out index);
            }
            else
            {
                return Ok(new { code = -1, msg = "缺少参数" });
            }
            string fileName = string.Empty;
            string fileExt = string.Empty;
            if (fileInfo.ContainsKey("name"))
            {
                fileName = fileInfo["name"].ToString();
            }
            if (fileInfo.ContainsKey("ext"))
            {
                fileExt = fileInfo["ext"].ToString();
            }
            if (string.IsNullOrEmpty(fileName))
            {
                return Ok(new { code = -1, msg = "文件名不能为空" });
            }
            //获取对应目录下文件,如果有,获取文件开始准备分段下载
            string filePath = GetFilePathAndName(fileBasePath, fileName);
            filePath = $"{filePath}{fileExt}";
            if (!System.IO.File.Exists(filePath))
            {
                return Ok(new { code = -1, msg = "文件尚未处理" });
            }
            using (var fs = new FileStream(filePath, FileMode.Open))
            {
                if (fs.Length <= 0)
                {
                    return Ok(new { code = -1, msg = "文件尚未处理" });
                }
                int shardSize = 1 * 1024 * 1024;//一次1M
                int count = (int)(fs.Length / shardSize);
                if ((fs.Length % shardSize) > 0)
                {
                    count += 1;
                }
                if (index > count - 1)
                {
                    return Ok(new { code = -1, msg = "无效的下标" });
                }
                fs.Seek(index * shardSize, SeekOrigin.Begin);
                if (index == count - 1)
                {
                    //最后一片 = 总长 - (每次片段大小 * 已下载片段个数)
                    shardSize = (int)(fs.Length - (shardSize * index));
                }
                byte[] datas = new byte[shardSize];
                await fs.ReadAsync(datas, 0, datas.Length);
                //fs.Close();
                return File(datas, "application/octet-stream");
            }
        }


        /// <summary>
        /// 获取上传文件的路径和名称
        /// </summary>
        /// <param name="fileBasePath">文件的基础路径</param>
        /// <param name="fileName">文件名称</param>
        /// <returns></returns>
        private static string GetFilePathAndName(string fileBasePath, string fileName)
        {
            string path = $".{fileBasePath}{DateTime.Now.ToString("yyyy-MM-dd")}/{fileName}";
            return path;
        }


        /// <summary>
        /// 文件流加密
        /// </summary>
        /// <param name="fileStream">文件流</param>
        /// <returns></returns>
        private string GetCryptoString(Stream fileStream)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] cryptBytes = md5.ComputeHash(fileStream);
            return GetCryptoString(cryptBytes);
        }

        /// <summary>
        /// 字节加密
        /// </summary>
        /// <param name="cryptBytes"></param>
        /// <returns></returns>
        private string GetCryptoString(byte[] cryptBytes)
        {
            //加密的二进制转为string类型返回
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < cryptBytes.Length; i++)
            {
                sb.Append(cryptBytes[i].ToString("x2"));
            }
            return sb.ToString();
        }

       
        #endregion 


    }//Class_end
}

②定义消息、请求上传文件实体

/***
*	Title:".NET Core WebApi" 项目
*		主题:消息实体
*	Description:
*		功能:XXX
*	Date:2021
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
*/
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
 
namespace Utils
{
    public class MessageEntity
    {
        private int _Code = 0;
        private string _Msg = string.Empty;
        private object _Data = new object();
 
        /// <summary>
        /// 状态标识
        /// </summary>
        public int Code { get => _Code; set => _Code = value; }
 
        /// <summary>
        /// 返回消息
        /// </summary>
        public string Msg { get => _Msg; set => _Msg = value; }
 
        /// <summary>
        /// 返回数据
        /// </summary>
        public object Data { get => _Data; set => _Data = value; }
 
    }//Class_end
}
/***
*	Title:".NET Core WebApi" 项目
*		主题:请求文件上传实体
*	Description:
*		功能:XXX
*	Date:2021
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
*/
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
 
namespace Utils
{
    public class RequestFileUploadEntity
    {
        private long _size = 0;
        private int _count = 0;
        private string _filedata = string.Empty;
        private string _fileext = string.Empty;
        private string _factoryid = string.Empty;
        private string _filename = string.Empty;
 
        /// <summary>
        /// 文件大小
        /// </summary>
        public long size { get => _size; set => _size = value; }
 
        /// <summary>
        /// 片段数量
        /// </summary>
        public int count { get => _count; set => _count = value; }
 
        /// <summary>
        /// 文件md5
        /// </summary>
        public string filedata { get => _filedata; set => _filedata = value; }
 
        /// <summary>
        /// 文件类型
        /// </summary>
        public string fileext { get => _fileext; set => _fileext = value; }
 
        /// <summary>
        /// 工厂id
        /// </summary>
        public string factoryid { get => _factoryid; set => _factoryid = value; }
 
        /// <summary>
        /// 文件名
        /// </summary>
        public string filename { get => _filename; set => _filename = value; }
 
 
    }//Class_end
}

三、运行程序测试

3.1、跨域错误

运行后发现是跨域错误【跨域内容介绍请查看:(CORS) 启用跨域请求 ASP.NET Core | Microsoft Docs

3.2、跨域设置

①在 【Startup--->ConfigureServices】方法下注册跨域策略

   #region   跨域设置
 
            //允许所有
            services.AddCors(options =>
            {
                options.AddPolicy("AllowAll", p =>
                {
                    p.SetIsOriginAllowed(hostName => true)
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials();
                });
            });
 
            只是示例,具体根据自身需要
            //services.AddCors(options =>
            //{
            //    options.AddPolicy("AllowSome", p =>
            //     {
            //         p.WithOrigins("https://www.baidu.com")
            //         .WithMethods("GET", "POST")
            //         .WithHeaders(HeaderNames.ContentType, "x-custom-header");
            //     });
            //});
 
            #endregion

②在【startup-->Configure】方法下使用注册好的的跨域策略

  #region   使用跨域 (UseCors必须配置为在对 UseRouting 和 UseEndpoints的调用之间执行)
            app.UseCors("AllowAll");
 
  #endregion 

?3.3、运行测试成功下载

四、上传文件使用的简单Html页面(之Layui)?

4.1、到Layui官网下载基础包

Layui - 经典开源模块化前端 UI 框架

?4.2、编写前端的文件分片上传界面和逻辑

①解压下载好的Layui框架包,然后修改test.html名为【uploadOrDownloadFile.html】

?②查看Layui上传文件示例编写分片上传文件逻辑

图片/文件上传模块文档 - Layui

<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <link rel="stylesheet" href="layui/css/layui.css">
</head>

<body>
    <div>
        <button type="button" class="layui-btn" id="btnDownload">下载</button>
    </div>
    <script src="layui/layui.js"></script>
    <script>
        var form;
        var $;
        var succeed = 0, shardCount = 0;
        var t1;

        layui.use(['upload', 'form', 'element'],
            function () {
                form = layui.form;
                $ = layui.$;
                var upload = layui.upload;
                var element = layui.element;
                var host = "http://localhost:47918/api/File/";
 
                $("#btnDownload").on("click", function (e) {
                    downloadRequest();
                });

                function downloadRequest() {
                    //请求文件
                    var data = {
                        name: 'Lexmark_XM1145_UsersGuide_sc',
                        ext: '.pdf',
                    };
                    //Ajax提交
                    $.ajax({
                        url: host + "RequestDownload",
                        type: "POST",
                        data: JSON.stringify(data),
                        type: "POST",
                        dataType: "json",
                        contentType: "application/json; charset=utf-8",
                        timeout: 36000,
                        success: function (res) {
                            if (res.code === -1) {
                                //layer.alert(decodeURIComponent(res.msg));
                                console.log(res);
                                return;
                            } else {
                                console.log(res.data);
                                //拿到文件开始分片下载
                                var count = res.data.count;
                                var filedata = res.data.fieldata;
                                var fileext = res.data.fileext;
                                var size = res.data.size;
                                maxCount = count - 1;
                                download();
                                window.clearInterval(t1);
                            }
                        }
                    });
                }

                var maxCount = 0;
                var index = 0;
                function download() {
                    var data = {
                        index: index,
                        name: 'Lexmark_XM1145_UsersGuide_sc',
                        ext: '.pdf',
                    };
                    //console.log(index);
                    //console.log(maxCount);
                    //Ajax提交
                    $.ajax({
                        url: host + "Download",
                        type: "POST",
                        data: JSON.stringify(data),
                        type: "POST",
                        dataType: "json",
                        contentType: "application/json; charset=utf-8",
                        timeout: 36000,
                        success: function (res) {
                            console.log(index + ":over");
                            index++;
                            if (index <= maxCount) {
                                download();
                            }
                        },
                        error: function (XMLHttpRequest, textStatus, errorThrown) {
                            console.log(textStatus);
                            console.log(index + ":errorover");
                            index++;
                            if (index <= maxCount) {
                                download();
                            } else {
                                console.log("下载完成");
                            }
                        }
                    });
                }
            });

    </script>
</body>

</html>

?参考文章:net core WebApi——文件分片下载 - AprilBlank

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-15 15:25:42  更:2021-08-15 15:28:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/26 17:00:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计