在要求输入邮箱的文本域,请填写真实的邮件地址。非真实邮件地址,将收不到回复信息。

ASP.NET Core Web Api实现大文件切/分片上传

.net core 清风 2370℃ 0评论

一年前的一个项目,有人反馈上传超时,超时原因是文件大小超出限制。由于原来维护项目的人员离开,现在上传超时的问题就有我来处理。解决方案也比较简单,就是切片上传。前端不想自己写了,就利用了上传组件,这个上传组件是百度WebUploaderWebUploader这个组件被使用的也比较广泛,为了省事就用它啦!

上传中

ASP.NET Core Web Api实现大文件切/分片上传-第0张图片
ASP.NET Core Web Api实现大文件切/分片上传-第1张图片

合并后

ASP.NET Core Web Api实现大文件切/分片上传-第2张图片

代码实现

前端代码

前端上传以及分片使用的百度上传组件WebUploader


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Home Page - WebUpload</title>

    
        <link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.css" />
    
    
    <link rel="stylesheet" href="/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" href="/">WebUpload</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" href="/">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" href="/Home/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        


        <main role="main" class="pb-3">
            <link rel="stylesheet" type="text/css" href="/webuploader/webuploader.css">
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script src="/webuploader/webuploader.js"></script>
    <div class="text-center">
        <h1 class="display-4">Welcome</h1>
        <div id="uploader" class="wu-example">
            <div class="container-fluid">
                <div class="col-md-10">
                    <div class="row">文件上传示例:</div>
                    <div class="row">
                        <div id="uploader" class="wu-example">
                            <!--用来存放文件信息-->
                            <div id="fileList" class="uploader-list"></div>
                            <div class="btns">
                                <div id="picker" class="btn btn-primary">选择文件</div>

                            </div>

                        </div>
                    </div>

                    <div class="row">

                    </div>
                    <div class="row">  <button id="ctlBtn" class="btn btn-default">开始上传</button></div>
                </div>
                <div>
                </div>
            </div>
        </div>
        <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
    </div>
<script>
    jQuery(function () {
        var $ = jQuery,
            $list = $('#fileList'),
            $btn = $('#ctlBtn'),
            state = 'pending',
            uploader, guid = {};
        uploader = WebUploader.create({
            // 不压缩image
            resize: false,

            // swf文件路径
            swf: "~/webuploader/Uploader.swf",

            server: 'http://localhost:8011/api/File/UplaodFile?isConvert=true',

            // 选择文件的按钮。可选。
            // 内部根据当前运行是创建,可能是input元素,也可能是flash.
            pick: '#picker',
            chunked: true

        });
        
        // 当有文件添加进来的时候
        uploader.on('fileQueued', function (file) {
            guid[file.id] = +(new Date());
            $list.append('<div id="' + file.id + '" class="item">' +
                '<h4 class="info">' + file.name + '</h4>' +
                '<p class="state">等待上传...</p>' +
                '</div>');
            uploader
                .md5File(file)
                .progress(function (percentage) {
                    // console.log("Percentage:", percentage);
                    $("#ctlBtn").hide();
                })
                // MD5计算完毕,可以点击上传了
                .then(function (fileMd5) {
                    file.fileMd5 = fileMd5;
                    $("#ctlBtn").show()
                    console.log(fileMd5);
                });

        });
        uploader.on('uploadBeforeSend', function (file, data, headers) {
            $.extend(data, { "fileId": guid[data.id], "fileMd5": file.file.fileMd5 });
            $.extend(headers, {
                "Authorization":"Bearer "
            });
            if (data.chunks) {
                data["chunkStart"] = file.start;
                data["chunkEnd"] = file.end;
            }
        });
        // 文件上传过程中创建进度条实时显示。
        uploader.on('uploadProgress', function (file, percentage) {

            var $li = $('#' + file.id),
                $percent = $li.find('.progress .progress-bar');
            // 避免重复创建
            if (!$percent.length) {
                $percent = $('<div class="progress progress-striped active">' +
                    '<div class="progress-bar" role="progressbar" style="width: 0%">' +
                    '</div>' +
                    '</div>').appendTo($li).find('.progress-bar');
            }
            $li.find('p.state').text('上传中');
            $percent.css('width', percentage * 100 + '%');

        });

        uploader.on('uploadSuccess', function (file) {
            $('#' + file.id).find('p.state').text('已上传');
        });

        uploader.on('uploadError', function (file) {
            $('#' + file.id).find('p.state').text('上传出错');
        });

        uploader.on('uploadComplete', function (file) {
            $('#' + file.id).find('.progress').fadeOut();
        });
        uploader.on('all', function (type) {
            if (type === 'startUpload') {
                state = 'uploading';
            } else if (type === 'stopUpload') {
                state = 'paused';
            } else if (type === 'uploadFinished') {
                state = 'done';
            }
            if (state === 'uploading') {
                $btn.text('暂停上传');
            } else {
                $btn.text('开始上传');
            }

        });

        $btn.on('click', function () {
            if (state === 'uploading') {
                uploader.stop();
            } else {
                uploader.upload();
            }
        });
    });
</script>

        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - WebUpload - <a href="/Home/Privacy">Privacy</a>
        </div>
    </footer>

    
        <script src="/lib/jquery/dist/jquery.js"></script>
        <script src="/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    
    
    <script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>

    
</body>
</html>

后台实现

后台使用的是ASP .NET Core Web API,实现方式也很简单。文件的校验,分块的校验等这里就省略了。
    
    [Route("api/[controller]")]
    public class FileController : Controller
    {
        public static readonly object locker = new object();
        [HttpPost]
        [Route("UplaodFile")]
        public IActionResult UplaodFile([FromForm] IFormCollection form)
        {
            try
            {
                string fileId = form["fileId"];
                if (string.IsNullOrWhiteSpace(fileId)||form.Files==null||form.Files.Count==0)
                {
                    return BadRequest();
                }
                string tempFileFolder = Path.Combine(AppContext.BaseDirectory, $"FileUploader/{fileId}");
                CreateFolder(tempFileFolder);
                if (form.ContainsKey("chunks"))
                {
                    string chunksString = form["chunks"];
                    int chunks = int.Parse(chunksString);
                    string chunkString = form["chunk"];
                    int chunk = int.Parse(chunkString);
                    string sizeString = form["size"];
                    long size = long.Parse(sizeString);
                    string chunkStartString = form["chunkStart"];
                    string chunkEndString = form["chunkEnd"];
                    string chunkMd5 = form["chunkMd5"];
                    string fileMd5 = form["fileMd5"];
                    string lastModifiedDate = form["lastModifiedDate"];
                    var file = form.Files.FirstOrDefault();
                    string name = file.FileName;
                    string ext = Path.GetExtension(name);
                    Stream stream = file.OpenReadStream();
                    byte[] bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    string factFilePath = Path.Combine(tempFileFolder, $"{name}");
                    string chunkFilePath = $"{factFilePath}.chunk{chunk}";
                    string chunkFileTempPath = $"{chunkFilePath}.temp";
                    System.IO.File.Delete(chunkFileTempPath);
                    FileStream fs = new FileStream(chunkFileTempPath, FileMode.Create);
                    BinaryWriter bw = new BinaryWriter(fs);
                    bw.Write(bytes);
                    bw.Flush();
                    bw.Close();
                    bw.Dispose();
                    fs.Close();
                    fs.Dispose();
                    stream.Close();
                    stream.Dispose();
                    System.IO.File.Move(chunkFileTempPath, chunkFilePath);
                    bool isMerge = true;
                    for (int i = 0; i < chunks; i++)
                    {
                        if (!System.IO.File.Exists($"{factFilePath}.chunk{i}"))
                        {
                            isMerge = false;
                            break;
                        }
                    }

                    if (isMerge)
                    {
                        lock (locker)
                        {
                            if (isMerge)
                            {
                                if (System.IO.File.Exists(factFilePath))
                                {
                                    return Ok();
                                }
                                var fileStream = new FileStream(factFilePath, FileMode.Create);
                                for (int i = 0; i < chunks; i++)
                                {
                                    string chunkFile = $"{factFilePath}.chunk{i}";
                                    var chunkBytes = System.IO.File.ReadAllBytes(chunkFile);
                                    fileStream.Write(chunkBytes, 0, chunkBytes.Length);
                                    fileStream.Flush();
                                    System.IO.File.Delete(chunkFile);//删除分块
                                }
                                fileStream.Close();
                                fileStream.Dispose();
                            }
                        }

                    }
                }
                else
                {
                    var file = form.Files.FirstOrDefault();
                    Stream stream = file.OpenReadStream();
                    byte[] bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    FileStream fileStream = new FileStream(Path.Combine(tempFileFolder, $"{file.FileName}"), FileMode.Create);
                    BinaryWriter bw = new BinaryWriter(fileStream);
                    bw.Write(bytes);
                    bw.Flush();
                    bw.Close();
                    bw.Dispose();
                    fileStream.Close();
                    fileStream.Dispose();
                    stream.Close();
                    stream.Dispose();
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex);
            }

            return Ok();
        }
        private void CreateFolder(string folderPath)
        {
            if (!Directory.Exists(folderPath))
            {
                Directory.CreateDirectory(folderPath);
            }
        }
        [HttpGet]
        [Route("Index")]
        public IActionResult Index()
        {
            return Ok();
        }
    }

示例下载

.NET CORE大文件分/切片上传示例



转载请注明:清风亦平凡 » ASP.NET Core Web Api实现大文件切/分片上传

喜欢 (8)or分享 (0)
支付宝扫码打赏 支付宝扫码打赏 微信打赏 微信打赏
头像
发表我的评论
取消评论

CAPTCHA Image
Reload Image
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址