.NetCore WebUploader 实现大文件分片上传

项目要求通过网站上传大文件,比如视频文件,通过摸索实现了文件分片来上传,然后后台进行合并。

使用了开源的前台上传插件WebUploader http://fex.baidu.com/webuploader/

WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。

采用大文件分片并发上传,极大的提高了文件上传效率。

话不多说,直接上代码,前端cshtml:

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <link href="~/js/webuploader/webuploader.css" rel="stylesheet" />
    <link href="~/js/bootstrap.min.css" rel="stylesheet" />
    <script src="~/js/jquery.min.js"></script>
    <script src="~/js/webuploader/webuploader.js"></script>
    <title>Upload</title>
    <script>
        jQuery(function () {
            var $ = jQuery,
                $list = $('#thelist'),
                $btn = $('#ctlBtn'),
                state = 'pending',
                fileMd5,
                flag = true,
                dataState,
                fm = [],
                fnum,
                Token,
                uploader;
            var FileExt = ["mpg", "mpeg", "mp4", "avi"];
            Token = '@ViewBag.Token';
            if (Token == '' || Token== 'undefined')
            {
                $("#uploader").hide();
                alert("登录超时,请重新登录。");
            }
            //监听分块上传过程中的三个时间点
            WebUploader.Uploader.register({
                "before-send-file": "beforeSendFile",
                "before-send": "beforeSend",
                "after-send-file": "afterSendFile",
            }, {
                    beforeSendFile: function (file) {
                        var startTime = new Date(file.lastModifiedDate);
                        fileName = file.name;
                        var deferred = WebUploader.Deferred(); 
                        (new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024)
                            .progress(function (percentage) {
                                console.log("正在读取文件");
                            })
                            .then(function (val) {
                                fileMd5 = val;
                                fm.push(fileMd5); 
                                deferred.resolve();
                            });
                        return deferred.promise();
                    },
                    //时间点2:如果有分块上传,则每个分块上传之前调用此函数
                    beforeSend: function (block) {
                        var deferred = WebUploader.Deferred();
 
                        //上传前ajax检测一下此文件块是否已经上传
 
                        this.owner.options.formData.fileMd5 = fileMd5;
                        this.owner.options.formData.chunk = block.chunk;
                        deferred.resolve();
                        return deferred.promise();
                    },
                    //时间点3:所有分块上传成功后调用此函数
                    afterSendFile: function (file) {
                        var deferred = $.Deferred();
                        $('#' + file.id).find('p.state').text('执行最后一步');
                        console.log(file);
                        console.log(file.guid);
                        $.ajax({
                            type: "POST",
                            url: "/api/v1/Check/FileMerge",
                            data: {
                                guid: file.guid,
                                fileMd5: fm[fnum],
                                fileName: file.name
                            },
                            cache: false,
                            async: false,
                            success: function (response) {
                                fnum++;
                                console.log(response);
                                if (response.success == true) {
                                    dataState = response;
                                    flag = true;
                                } else {
                                    flag = false;
                                }
                                deferred.resolve();
                            },
                            error: function () {
                                fnum++;
                                dataState = undefined;
                                flag = false;
                                deferred.reject();
                            }
                        });
 
                        return deferred.promise();
                    }
                });
            uploader = WebUploader.create({
                resize: false,
                fileNumLimit: 10,
                swf: '/js/Uploader.swf',
                server: '/api/v1/Check/FileSave',
                pick: '#picker',
                chunked: true,
                chunkSize: 10 * 1024 * 1024,
                chunkRetry: 5
                //, formData: {
                //    guid: GUID
                //}
            });
            uploader.on('beforeFileQueued', function (file) {
                var isAdd = false;
                for (var i = 0; i < FileExt.length; i++) {
                    if (file.ext == FileExt[i]) {
                        file.guid = WebUploader.Base.guid();
                        isAdd = true;
                        break;
                    }
                }
                return isAdd;
            });
            uploader.on('uploadBeforeSend', function (object, data, headers) {
                //console.log(object);
                headers.Authorization =Token;
                data.guid = object.file.guid;
            });
            // 当有文件添加进来的时候
            uploader.on('fileQueued', function (file) {
                $list.append('<div id="' + file.id + '" class="item">' +
                    '<h4 class="info">' + file.name + '</h4>' +
                    '<input type="hidden" id="h_' + file.id + '" value="' + file.guid + '" />' +
                    '<p class="state">等待上传...</p>' +
                    '</div>');
            });
 
            // 文件上传过程中创建进度条实时显示。
            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) {
                if (dataState == undefined) {
                    $('#' + file.id).find('p.state').text('上传失败');
                    $('#' + file.id).find('button').remove();
                    $('#' + file.id).find('p.state').before('<button id="retry" type="button" class="btn btn-primary fright retry pbtn">重新上传</button>');
                    flag = false;
                    file.setStatus('error');
                }
                if (dataState.success == true) {
                    $('#' + file.id).find('p.state').text('已上传');
                    $('#' + file.id).find('button').remove();
 
                } else {
                    $('#' + file.id).find('p.state').text('上传失败');
                    flag = false;
                }
            });
 
            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 === 'done') {
                    $btn.text('继续上传');
                } else if (state === 'uploading') {
                    $btn.text('暂停上传');
                } else {
                    $btn.text('开始上传');
                }
            });
            $btn.on('click', function () {
                if (state === 'uploading') {
                    uploader.stop();
                } else if (state == 'done') {
                    window.location.reload();
                }
                else {
                    uploader.upload();
                }
            });
        });
    </script>
</head>
<body>
    <div id="uploader" class="wu-example">
        <span style="color:red">只能上传mpg、mpeg、mp4、avi格式的视频文件</span>
        <div id="thelist" class="uploader-list"></div>
        <div class="btns">
            <div id="picker" class="webuploader-container"><div class="webuploader-pick">选择文件</div><div style="position: absolute; top: 0px; left: 0px; width: 88px; height: 34px; overflow: hidden; bottom: auto; right: auto;"><input type="file" name="file" class="webuploader-element-invisible" multiple="multiple"><label style="opacity: 0; width: 100%; height: 100%; display: block; cursor: pointer; background: rgb(255, 255, 255);"></label></div></div>
            <button id="ctlBtn" class="btn btn-default">开始上传</button> 
        </div>
    </div>
</body>
</html>

服务端代码:

#region 上传视频
        public IActionResult Upload()
        {
            ViewBag.Token = HttpContext.Request.Headers["Authorization"];//获取认证信息,传递给前台,方便Ajax请求时提供
            return View();
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> FileSave()
        {
            var date = Request;
            var files = Request.Form.Files;
            long size = files.Sum(f => f.Length);
            foreach (var formFile in files)
            {
                if (formFile.Length > 0)
                {
                    string fileExt = formFile.FileName.Substring(formFile.FileName.IndexOf('.')); //文件扩展名,不含“.”
                    long fileSize = formFile.Length; //获得文件大小,以字节为单位
                    //string newFileName = Guid.NewGuid().ToString() + "." + fileExt; //随机生成新的文件名
                    string DirPath = Path.Combine(_uploadConfig.TmpPath, Request.Form["guid"]);
                    if (!Directory.Exists(DirPath))
                    {
                        Directory.CreateDirectory(DirPath);
                    }
                    var filePath = DirPath + "/" + Request.Form["chunk"] + fileExt;
                    using (var stream = new FileStream(filePath, FileMode.Create))
                    {
                        await formFile.CopyToAsync(stream);
 
                    }
                }
            }
            return Ok(new { count = files.Count, size });
        }
 
        /// <summary>
        /// 合并请求
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> FileMerge()
        {
            bool ok = false;
            string errmsg = "";
            try
            {
                var temporary = Path.Combine(_uploadConfig.TmpPath, Request.Form["guid"]);//临时文件夹
                string fileName = Request.Form["fileName"];//文件名
                string fileExt = Path.GetExtension(fileName);//获取文件后缀
                var files = Directory.GetFiles(temporary);//获得下面的所有文件
              
                var finalFilePath = Path.Combine(_uploadConfig.UpLoadPath + fileName);//最终的文件名
                //var fs = new FileStream(finalFilePath, FileMode.Create);
                using (var fs = new FileStream(finalFilePath, 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);//删除分块
                    }
                    Directory.Delete(temporary);//删除文件夹
                    ok = true;
                }
            }
            catch (Exception ex)
            {
                ok = false;
                errmsg = ex.Message;
                log4net.Error(errmsg);
            }
            if (ok)
            {
                return Ok(new { success = true, msg = "" });
            }
            else
            {
                return Ok(new { success = false, msg = errmsg }); ;
            }
        }
 
        #endregion

目前浏览器貌似对FLASH并不是很友好,可以考虑其它插件,或者有更好的方式可以留言给我,希望以上代码可以对您有所帮助。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 .NET Core 中,实现数据库动态列的一种常见方法是使用 NoSQL 数据库,如 MongoDB。MongoDB 是一个文档型数据库,允许你在同一个集合中存储不同结构的文档。 要实现动态列,你可以定义一个基本的文档结构,然后在需要时动态添加或删除字段。下面是一个使用 .NET Core 和 MongoDB 的示例代码: 首先,确保你已经安装了 MongoDB 驱动程序包。你可以在项目的 NuGet 包管理器中搜索并安装 "MongoDB.Driver"。 ```csharp using MongoDB.Bson; using MongoDB.Driver; // 定义要存储的文档结构 class MyDocument { public ObjectId Id { get; set; } public BsonDocument DynamicColumns { get; set; } } // 连接到 MongoDB var client = new MongoClient("mongodb://localhost:27017"); var database = client.GetDatabase("mydatabase"); var collection = database.GetCollection<MyDocument>("mycollection"); // 创建一个新文档并添加动态列 var newDocument = new MyDocument { DynamicColumns = new BsonDocument { { "column1", "value1" }, { "column2", 123 }, { "column3", true } } }; // 插入文档到集合中 collection.InsertOne(newDocument); // 查询文档 var query = Builders<MyDocument>.Filter.Eq("_id", newDocument.Id); var result = collection.Find(query).FirstOrDefault(); // 获取动态列的值 var value = result.DynamicColumns["column1"]; // "value1" ``` 在上面的示例中,我们定义了一个名为 `MyDocument` 的类作为文档的模型。该类具有一个动态列 `DynamicColumns`,它是一个 `BsonDocument` 类型,可以存储任意键值对。 通过使用 MongoDB 驱动程序的 `InsertOne` 方法将文档插入到集合中,并使用 `Find` 方法查询文档。然后,我们可以使用 `BsonDocument` 的索引器语法访问动态列的值。 请注意,这种方法适用于需要存储不同结构的文档,并且需要在运行时动态添加或删除字段的情况。如果你只是想在关系型数据库中添加新列,那么你需要进行数据库迁移或者使用 ORM 工具来处理动态列。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值