松鼠的博客

导航

WEB上传大文件

众所皆知,web上传大文件,一直是一个痛。上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的。

本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路。下面贴出简易DEMO源码分享:

前端页面:

@{

    ViewBag.Title = "Upload";

}

<h2>Upload</h2><table class="table table-striped">

    <tr>

        <td><input type="file" id="file"  onchange="selfile()" /></td>

        <td><input type="button" value="上传" onclick="uploading()"/></td>

    </tr>

    <tr>

        <td colspan="2">文件信息:<span id="fileMsg"></span></td>

    </tr>

    <tr>

        <td colspan="2">当前进度:<span id="upsize"></span></td>

    </tr></table><script src="~/Scripts/myUploader.js"></script><script type="text/javascript">

   //guid

    var guid = "@Guid.NewGuid()";

    var uploader;

    function selfile() {

        var f =  $("#file")[0].files[0];

        uploader = new SupperUploader("@Url.Action("RecvUpload")", f, guid, (1024*1024));

        $("#fileMsg").text("文件名:" + uploader.fileName + "文件类型:" + uploader.fileType + "文件大小:" + uploader.fileSize + "字节");

    }

    function uploading() {

        uploader.UploadFun(function () {

            $("#upsize").text(uploader.upedSize);

        })

    }</script>

SupperUploader是我自己封装的JS插件,源码如下:

 var SupperUploader = function (uploadUrl, file, guid, cutSize) {

    this.file = file;

    //文件大小

    this.fileSize = file.size;

    //文件类型

    this.fileType = file.type;

    //文件路径

    this.fileName = file.name;

    //guid

    this.guid = guid;

    //分片大小

    this.cutSize = cutSize,

    //已上传

    this.upedSize = 0;

    //开始位置

    this.startIndex = 0;

    //结束位置

    this.endIndex = 0;

    //序号

    this.indexr = 0;

    //上传路径

    this.uploadUrl = uploadUrl;

    //合并结果

    this.merged = false;

};

SupperUploader.prototype = {

    UploadFun: function (uploadCallBack) {

        if (this.merged)

            return;

        var thisobj = this;

        $.ajax({

            type: "POST",

            url: thisobj.uploadUrl,

            enctype: 'multipart/form-data',

            data: thisobj.CutFileFun(),

            processData: false,

            contentType: false,

            success: function (res) {

                if (res == "success") {

                    if (thisobj.upedSize == thisobj.fileSize) {

                        thisobj.merged = true;

                        alert("已成功上传!")

                        return;

                    }

                    thisobj.upedSize += thisobj.cutSize;

                    if (thisobj.upedSize > thisobj.fileSize)

                        thisobj.upedSize = thisobj.fileSize;

                    thisobj.indexr+=1;

                    //执行回调函数                    uploadCallBack();

                    //继续调用上传                    thisobj.UploadFun(uploadCallBack);

                }

            }

        });

    },

    CutFileFun: function () {

        var formData = null;

        if (this.upedSize < this.fileSize) {

            this.startIndex = this.upedSize;

            this.endIndex = this.startIndex + this.cutSize;

            if (this.endIndex > this.fileSize) {

                this.endIndex = this.fileSize;

            }

            var currentData = this.file.slice(this.startIndex, this.endIndex);

            formData = new FormData();

            formData.append("file", currentData);

            formData.append("index", this.indexr);

            formData.append("fname", this.fileName);

            formData.append("guid", this.guid);

            formData.append("ismerge", this.fileSize == this.endIndex);

           

        }

        return formData;

    }

};

后端代码,此Demo是基于MVC架构的:

 

        [HttpGet]

        public ActionResult Upload() {

            return View();

        }

 

        [HttpPost]

        public ActionResult RecvUpload(){

            try

            {

                string fileName = Request["fname"];

                string index = Request["index"];

                string guid = Request["guid"];

                var file = Request.Files[0];

                var ismerge = Request["ismerge"];

                string tempDirpath = "~/Content/temp/" + guid + "/";

                string savepath = tempDirpath + index + "_" + fileName;

                //合并文件

                if (bool.Parse(ismerge))

                {

                    //获取所有分割文件

                    var files = System.IO.Directory.GetFiles(Server.MapPath(tempDirpath));

                    //文件FILEINFO

                    var infos = files.Select(x => new FileInfo(x)).ToList().OrderBy(x=>x.LastWriteTime).ToList();

                    //合并文件流

                    FileStream mergefs = new FileStream(Server.MapPath("~/Content/temp/" + fileName),FileMode.Append);

                    BinaryWriter bw = new BinaryWriter(mergefs);

                    FileStream tempfs = null;

                    BinaryReader tempbr= null;

                    infos.ToList().ForEach(f =>

                    {

                        tempfs = new FileStream(f.FullName, FileMode.Open);

                        tempbr = new BinaryReader(tempfs);

                        bw.Write(tempbr.ReadBytes((int)tempfs.Length));

                        tempfs.Close();

                        tempbr.Close();

                    });

                    bw.Close();

                    mergefs.Close();

                    //删除分块文件

                    infos.ForEach(f =>{

                        System.IO.File.Delete(f.FullName);

 

                    });

                    return Json("success");

                }

                if (!System.IO.Directory.Exists(Server.MapPath(tempDirpath))){

                    System.IO.Directory.CreateDirectory(Server.MapPath(tempDirpath));

                }

                using (FileStream fs = new FileStream(Server.MapPath(savepath), FileMode.CreateNew))

                {

 

                    using (Stream stream = file.InputStream)

                    {

                        byte[] buffer = new byte[stream.Length];

                        stream.Read(buffer, 0, (int)stream.Length);

                        fs.Write(buffer, 0, buffer.Length);

                    }

                }

                return Json("success");

 

            }

            catch (Exception e)

            {

 

                return Json(e.Message);

            }

           

        }

在此分享!希望多多指正~

后端代码逻辑大部分是相同的,目前能够支持MySQL,Oracle,SQL。在使用前需要配置一下数据库,可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/07/java超大文件上传与下载/


posted on 2019-08-14 10:48  Xproer-松鼠  阅读(178)  评论(0编辑  收藏  举报