在写网站时,经常会用到文件上传这个。一般的图片上传什么的直接用fileupload倒也简单。但是遇到大文件上传就头大了。以前我也是使用别人的一个控件,但是总觉得不怎么好,也不是开源,就还是想自己写一个。

实现过程也还是挺简单的,但是仅支持html5,低版本的浏览器就不支持了。

这里用的是html5的formdata来获取文件,并用slice分割后,逐个上传。

JS:

 function ReadFile(files) {
        var len = files.length;
        var per;
        var uploadLength = new Array(len);
        for (var file_index = 0; file_index < len; file_index++) {
            $('.list' + file_index + ' .pro .probar').width(0);
            var file = files[file_index];
            var size = file.size;
            var shardSize = 2097152;//以2M为一块,这个可以自定义
            var shardCount = Math.ceil(size / shardSize);
            var tryTimes = 0;
            uploadLength[file_index] = 0;//已完成的文件长度
            for (var bock_index = 0; bock_index < shardCount; bock_index++) {
                if (tryTimes >= 3) {
                    //失败三次跳出循环避免死循环,显示失败信息
                    tryTimes = 0;
                    $('.list' + file_index + ' .pro .probar').addClass("displayN");
                    $('.list' + file_index + ' .pro').append("<div>上传失败</div>");
                    break;
                }
                
                var start = bock_index * shardSize;
                var end = Math.min(size, start + shardSize);
                var formdata = new FormData();
                formdata.append("data", file.slice(start, end));
                formdata.append("name", file.name);
                formdata.append("total", shardCount);
                formdata.append("index", bock_index);
                formdata.append("file_index", file_index);
                formdata.append("file_length", size);
                formdata.append("shardSize", shardSize);
                $.ajax({
                    url: "Upload",
                    type: "post",
                    data: formdata,
                    async: true,
                    processData: false,//不对form进行处理
                    contentType: false,//指定为false才能形成正确的Content-Type
                    success: function (data, textStatus) {
                        var findex = data.findex;
                        if (data.Error == 0) {
                            uploadLength[findex] += data.successlength;
                            var thislength = data.file_length;
                            per = Math.ceil(uploadLength[findex] / thislength * 100) + "%";
                            $('.list' + findex + ' .pro .probar').width(per);
                            if (per != '100%')
                                $('.list' + findex + ' .w100 .spprocess').text(per);
                            else {
                                $('.list' + findex + ' .w100 .spprocess').text('完成');
                            }
                        }
                        else if (data.Error == 1) {
                            //发生错误,重新上传
                            bock_index--;
                            tryTimes++;
                        } else {
                            tryTimes = 3;
                            $('.list' + findex + ' .pro .probar').addClass("displayN");
                            $('.list' + findex + ' .pro').append("<div>上传失败</div>");
                        }
                        
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        //发生错误,重新上传
                        bock_index--;
                        tryTimes++;
                    }
                });
            }
        }
    }
        这里确实有几个需要注意的地方。

         首选要明确这个是异步上传,它与同步上传的差别还是非常大的。认真看代码的可以发现,里面一些变量是传入后台,又从后台获取。这么做的一个目的,是保持变量的值。比如:

        在for循环里的几个变量如file_index和bock_index,你在浏览器中断点ajax的success查看就会发现,它们并非是从0-n的依次变化,由于是异步上传,for并不会等待ajax的操作结束,ajax向后台提交后,for就继续往下走,往往等ajax收到后台返回的信息后,for循环已经结束,这里候在success里获取的bock_index或者file_index都是最后一个。只有提交后台后,再由后台发回来,才能够取到正确的信息。如果是同步上传,这些就是不必要的了。或者说我们不需要显示上传进度也不需要这么做。

error里面的意思是发生错误后重新上传,这个在同步上传中有用,但异步操作的话,我觉得也是没什么用的。可以删去。

.net:

public ActionResult Upload()
        {
            string name = Request.Form["name"];
            int total = Int32.Parse(Request.Form["total"]);
            int index = Int32.Parse(Request.Form["index"]);
            System.Web.HttpPostedFileBase data = Request.Files["data"];
            
            long file_length = long.Parse(Request.Form["file_length"]);
            int shardSize = Int32.Parse(Request.Form["shardSize"]);
            string dir = Server.MapPath("~/upload/");
            string file_name = null;
            FileStream fs = null;
            int bock_length = 0;
            try
            {
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
                //块文件的存放
                //string bock_name = Path.Combine(dir, name + "_" + index);
                //data.SaveAs(bock_name);
                //实际文件的路径
                file_name = Path.Combine(dir, name);
                //写入文件中
                if (!System.IO.File.Exists(file_name))
                {
                    //构建与要上传文件大小相同的文件
                    fs = new FileStream(file_name, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
                    fs.SetLength(file_length);
                    fs.Close();
                }
                Stream st = data.InputStream;
<span style="white-space: pre;">		</span>long getLength = st.Length;
                byte[] bytesBock = new byte[getLength];
                st.Read(bytesBock, 0, (int)getLength);
                fs = new FileStream(file_name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                int offset = index * shardSize;//写入文件偏移量
                //bytesBock = System.IO.File.ReadAllBytes(bock_name);
                bock_length = bytesBock.Length;
                //System.IO.File.Delete(bock_name);
                //写入字节流
                fs.Seek(offset, SeekOrigin.Begin);//移动到要写入的位置
                fs.Write(bytesBock, 0, bock_length);
                fs.Close();
            }
            catch (Exception e)
            {
                fs.Close();
                if (file_name != null)
                    WriteLog(file_name, index, "save" + e.ToString());
                else WriteLog("noname", index, e.ToString());
                return Json(new { Error = 1, findex = Request.Form["file_index"] });
            }

            return Json(new { Error = 0, findex = Request.Form["file_index"],index=index, successlength = bock_length, file_length = file_length });
        }
        这个类似于迅雷的下载,先创建一个与要上传的文件大小的文件,然后再往里面相应的位置写入数据。这里的关键是offset,这个偏量。

这里比较关键的还是Write(byte[] array, int offset, int count)这个方法,offset这个偏移量,我一开始时也搞成了要写入文件的偏移量,后来查了才知道,原来是待入的byte[]的偏移量。

        一些不懂的方法可以去查查。



Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐