分割檔案上傳V1 ( 多檔生成最後整併 )

  • 1480
  • 0

因公司有需求,需要將一個大檔切割成諸多檔上傳,不透過以往upload一次就把檔案大小過大的檔案一次往Server丟,可能造成的一些頻寬、記憶體等問題。
進度條的判斷,可利用signalR在進行搬檔時回傳client端進行顯示,或是直接顯示切割丟檔的進度。

 

 Javascript 

<script>
        $(function () {
            page.init();
        });

        var page = {
            init: function () {
                $("#upload").click($.proxy(this.upload, this));
            },
            upload: function () {

                var file = $("#file")[0].files[0],
                    name = file.name,
                    size = file.size,
                    stamp = Date.now(),
                    succeed = 0;

                var ext
                var extIndex = name.lastIndexOf('.');
                if (extIndex != -1) {
                    ext = name.substr(extIndex + 1, name.length);
                }

                var shardSize = 2 * 1024 * 1024,    //以2MB为主
                    shardCount = Math.ceil(size / shardSize);  //總切片數

                var deferreds = [];
                var res = [];
                for (var i = 0; i < shardCount; ++i) {

                    //計算起始與結束位置
                    var start = i * shardSize,
                        end = Math.min(size, start + shardSize);

                    //FormData
                    var form = new FormData();
                    form.append("data", file.slice(start, end));  //slice方法用于切出文件的一部分
                    form.append("name", name);
                    form.append("stamp", stamp);
                    form.append("ext", ext);
                    form.append("size", size);
                    form.append("total", shardCount);  //總數
                    var indexx = i + 1;
                    form.append("index", indexx);        //目前是第幾個檔

                    //Ajax提交
                    deferreds.push(
                        $.ajax({
                            url: "@Url.Action("Upload2", "FileHandle")",
                            type: "POST",
                            data: form,
                            async: true,        //是否採非同步進行
                            processData: false,
                            contentType: false,
                            success: function (data) {
                                ++succeed;
                                $("#output").text(succeed + " / " + shardCount);
                            }
                        })
                    );
                }

                $.when.apply($, deferreds).done(function () {

                     //待切割檔案上傳全處理完成後,處理後續相關動作
                     //我自己的處理方式是將input file設為零,只做form data的處理

                });
            }
        };
    </script>

Html

<input type="file" id="file" />
<button id="upload">上傳</button>
<span id="output" style="font-size:12px">等待</span>

C# Controller

        public ActionResult Upload()
        {

            string savename = Request["name"];
            string stamp = Request["stamp"];
            string ext = Request["ext"];

            int total = Convert.ToInt32(Request["total"]);
            int index = Convert.ToInt32(Request["index"]);
            var data = Request.Files["data"];

            //保存在磁碟上
            string dir = "D:/slicefile";
            string file = Path.Combine(dir, stamp + "_" + index);
            data.SaveAs(file);

            //用目的檔案的個數來判斷是否該開始合併
            DirectoryInfo dirInfo = new DirectoryInfo(dir);
            var sliceCount = dirInfo.GetFiles(stamp + "_*").Length;

            //透過stamp判斷檔案數如同總數則合併
            if (sliceCount == total)
            {
                try
                {
                    file = Path.Combine(dir, stamp + "." + ext);
                    var fs = new FileStream(file, FileMode.Create);
                    for (int i = 1; i <= total; ++i)
                    {
                        string part = Path.Combine(dir, stamp + "_" + i);
                        var bytes = System.IO.File.ReadAllBytes(part);
                        fs.Write(bytes, 0, bytes.Length);
                        bytes = null;
                        System.IO.File.Delete(part);
                    }
                    fs.Close();
                }
                catch (Exception)
                {
                    //此做簡化處理,因非同步速度過快,因此導致檔案在判斷總數上會出現異常
                    //利用try catch進行包住避開
                    return Json(new { Error = 0 });
                }
            }

            return Json(new { Error = 0 });

        }