文件分片多線程上傳

前兩天有人在群里問文件分片上傳如何實現(xiàn),當(dāng)時沒多想就直接說js對文件進(jìn)行分片仅政,上傳分片,后端接受分片盆驹,js判斷分片上傳完成圆丹,發(fā)起合并請求,后端合并文件即可躯喇。后來就用webuploader插件實現(xiàn)了一下辫封,然后他問我可不可以幫他寫一個不用插件的分片上傳,所以才有了下面的文章廉丽。


前端代碼

file.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>分片多線程上傳--夢中程序員出品</title>
    <style>
        #info {
            width: 400px;
            height: 200px;
        }
        .percent_bg {
            position: relative;
            width: 400px;
            height: 20px;
            border: 1px solid #ccc;
        }
        .percent {
            position: absolute;
            width: 0%;
            height: 100%;
            background: #0b821c;
        }
        .percent_num {
            position: absolute;
            width: 100%;
            height: 100%;
            text-align: center;
        }
    </style>
</head>
<body>
    <h3>文件分片多線程上傳倦微,上傳成功后返回url</h3>
    <p>選擇文件后自動上傳</p>
    線程數(shù):
    <select id="thread_num">
        <option value="1">1</option>
        <option value="2" selected>2</option>
        <option value="3">3</option>
        <option value="5">5</option>
        <option value="10">10</option>
    </select><br>
    <input type="file" id="file"><br>
    <textarea id="info"></textarea>
    <div class="percent_bg">
        <div class="percent"></div>
        <div class="percent_num">0%</div>
    </div>

    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
        // 文件分片信息
        var blocksInfo = {
            "bufferSize": 512 * 1024, // 512K為一片
            "blocks": [], // 所有分片文件
            "threadNum": 1, // 上傳線程數(shù)
            "filename": "" // 文件名字
        };

        // 線程信息
        var threadInfo = {
            "index": 0, // 準(zhǔn)備上傳的分片索引
            "activeThread": 0, // 幾個線程在工作
            "sendBlocksNum": 0 // 分片上傳完成個數(shù)
        };

        // 選擇文件觸發(fā)change事件
        $("#file").change(function () {
            var file = document.getElementById("file").files[0];
            setBlocksInfo(file); // 設(shè)置分片信息
            // 啟動線程
            var realThread = Math.min(blocksInfo.threadNum, blocksInfo.blocks.length); // 如果分片數(shù)小于線程則只運(yùn)行分片數(shù)的線程
            showInfo("分片完成,分了" + blocksInfo.blocks.length + "片正压,線程數(shù)為:" + realThread);
            showInfo("------------");
            for (var i = 0;i < realThread;i++) {
                threadInfo.activeThread++;
                // 應(yīng)該用js的線程插件來控制線程欣福,我們就不用了,我們知道ajax的異步就是用多線程發(fā)送請求的焦履,我們使用ajax來模擬多線程拓劝,這塊雖然函數(shù)是單線程,進(jìn)入事件隊列裁良,但是運(yùn)行到ajax則是多線程了
                startThread(i);
            }
        });

        // 設(shè)置分片信息
        function setBlocksInfo(file)
        {
            blocksInfo.threadNum = $("#thread_num").val();
            blocksInfo.filename = file.name;
            var startByte = endByte = 0;
            while (true) {
                if (endByte + blocksInfo.bufferSize >= file.size) {
                    endByte = file.size;
                } else {
                    endByte = startByte + blocksInfo.bufferSize;
                }
                var block = file.slice(startByte, endByte);
                blocksInfo.blocks.push(block);
                startByte = endByte;
                if (endByte >= file.size) {
                    break;
                }
            }
        }

        // 顯示運(yùn)行信息
        function showInfo(info)
        {
            var msg = $("#info").val() + info + "\r\n";
            $("#info").val(msg);
            var scrollTop = $("#info")[0].scrollHeight;
            $("#info").scrollTop(scrollTop);
        }

        // 線程運(yùn)行
        function startThread(i)
        {
            if (threadInfo.index >= blocksInfo.blocks.length) {
                showInfo("線程" + i + "結(jié)束");
                threadInfo.activeThread--;
                if (threadInfo.activeThread == 0) {
                    showInfo("------------");
                    showInfo("分片上傳完成凿将,正在處理分片");
                    combineBlocks(); // 發(fā)起合并分片請求
                }
                return;
            }
            uploadBlock(i, threadInfo.index); // 使用指定線程上傳指定分片
            threadInfo.index++; // 準(zhǔn)備下一個分片
        }

        // 上傳分片
        function uploadBlock(i, index)
        {
            showInfo("線程" + i + "開始:上傳" + index + "分片");
            // 組裝上傳信息,ajax上傳文件需要formdata
            var fd = new FormData();
            fd.append("index", index); // 上傳分片序號
            fd.append("file", blocksInfo.blocks[index]);
            $.ajax({
                url: "upload.php",
                type: "post",
                data: fd,
                dataType: "json",
                contentType: false, // 文件上傳和參數(shù)傳遞請求頭和請求體都不一樣价脾,ajax設(shè)置false就可以
                processData: false, // 有文件上傳牧抵,不對參數(shù)序列化
                success: function (data) {
                    threadInfo.sendBlocksNum++; // 設(shè)置這個分片已經(jīng)上傳完成
                    showPercent(); // 上傳進(jìn)度條
                    showInfo("分片" + index + "上傳完成,線程" + i + "即將上傳下一個分片");
                    startThread(i); // 啟動線程繼續(xù)下一個分片上傳
                }
            });
        }
    
        // 發(fā)起合并分片請求
        function combineBlocks()
        {
            $.ajax({
                url: "upload.php",
                type: "post",
                data: {"act": "combine", "blocks": blocksInfo.blocks.length, "filename": blocksInfo.filename},
                dataType: "json",
                success: function (data) {
                    showInfo("分片數(shù)據(jù)處理完成侨把,任務(wù)結(jié)束犀变,URL:" + data.url);
                }
            });
        }

        // 進(jìn)度條
        function showPercent()
        {
            var percent = parseInt(threadInfo.sendBlocksNum / blocksInfo.blocks.length * 100);
            $(".percent").stop(true, true).animate({"width": percent + "%"}, 10);
            $(".percent_num").html(percent + "%");
        }
    </script>
</body>
</html>

后端代碼

upload.php

<?php
class Uploader
{
    public $tmpPath = __DIR__ . '/tmp/'; // 分片目錄
    public $filePath = __DIR__ . '/file/'; // 合并分片目錄,這倆個目錄需手動創(chuàng)建秋柄,你也可以使用mkdir創(chuàng)建

    public function upload()
    {
        if (isset($_POST['act']) && $_POST['act'] == 'combine') {
            $blocks = $_POST['blocks'];
            $filename =time() . $_POST['filename'];
            $file = fopen($this->filePath . $filename, 'a+');
            for ($i = 0;$i < $_POST['blocks'];$i++) {
                $chunkFile = fopen($this->tmpPath . $i, 'r');
                fwrite($file, fread($chunkFile, filesize($this->tmpPath . $i)));
                fclose($chunkFile);
                unlink($this->tmpPath . $i);
            }
            fclose($file);
            $data = [
                'url' => "http://" . $_SERVER['HTTP_HOST'] . '/file/' . $filename 
            ];
        } else {
            $index = $_POST['index']; // 分片索引
            move_uploaded_file($_FILES['file']["tmp_name"], $this->tmpPath . $index);
            $data = [
                'code' => 1
            ];
        }
        return json_encode($data);
    }
}

echo (new Uploader)->upload();

有疑問可聯(lián)系QQ305530751

夢中程序員系列教程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末获枝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子骇笔,更是在濱河造成了極大的恐慌省店,老刑警劉巖嚣崭,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異懦傍,居然都是意外死亡雹舀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門粗俱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來说榆,“玉大人,你說我怎么就攤上這事寸认∏┎疲” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵偏塞,是天一觀的道長唱蒸。 經(jīng)常有香客問我,道長烛愧,這世上最難降的妖魔是什么油宜? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮怜姿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疼燥。我一直安慰自己沧卢,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布醉者。 她就那樣靜靜地躺著但狭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撬即。 梳的紋絲不亂的頭發(fā)上立磁,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機(jī)與錄音剥槐,去河邊找鬼唱歧。 笑死,一個胖子當(dāng)著我的面吹牛粒竖,可吹牛的內(nèi)容都是我干的颅崩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蕊苗,長吁一口氣:“原來是場噩夢啊……” “哼沿后!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起朽砰,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤尖滚,失蹤者是張志新(化名)和其女友劉穎喉刘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漆弄,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡睦裳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了置逻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片推沸。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖券坞,靈堂內(nèi)的尸體忽然破棺而出鬓催,到底是詐尸還是另有隱情,我是刑警寧澤恨锚,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布宇驾,位于F島的核電站,受9級特大地震影響猴伶,放射性物質(zhì)發(fā)生泄漏课舍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一他挎、第九天 我趴在偏房一處隱蔽的房頂上張望筝尾。 院中可真熱鬧,春花似錦办桨、人聲如沸筹淫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽损姜。三九已至,卻和暖如春殊霞,著一層夾襖步出監(jiān)牢的瞬間摧阅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工绷蹲, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留棒卷,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓瘸右,卻偏偏與公主長得像娇跟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子太颤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容