JS PHP 分片上传

PHP·Javascript · 2023-11-15
  1. 前端页面将文件分块上传。
  2. 后端在最后一条时进行合并处理。
  3. 最后一条需要等待前面的上传完毕再执行,因此用Promise.all() 函数处理。

前端js

    function getRandChars(e) {
        e = e || 32;
        var t = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678",
            a = t.length,
            n = "";
        for (i = 0; i < e; i++) n += t.charAt(Math.floor(Math.random() * a));
        return n
    }

    function uploadFile(url, blob, callback) {
        var perFileSize = 2097152; // 2 * 1024 * 1024
        var blobParts = Math.ceil(blob.size / perFileSize);
        const fileName = getRandChars();
        layer.msg("文件上传中", {
            icon: 16,
            time: -1
        })
        function request(url, fd, callback) {
            return new Promise((resolve, reject) => {
                $.ajax({
                    type: 'POST',
                    url: url,
                    data: fd,
                    processData: false,
                    contentType: false
                }).done(function (res) {
                    resolve(res);
                }).fail(function (err) {
                    reject(err);
                });
            });
        }
        let beforeRequests = []
        for (var i = 0; i < blobParts; i++) {
            (function (i) {
                var fd = new FormData();
                var _blob = blob.slice(i * perFileSize, (i + 1) * perFileSize);
                fd.append('file', _blob);
                fd.append('fileName', fileName);
                fd.append('index', i + 1);
                fd.append('total', blobParts);
                if (i + 1 < blobParts) {
                    beforeRequests.push(request(url, fd, callback))
                } else {
                    Promise.all(beforeRequests).then(responses => {
                        // 所有请求完成后执行的操作
                        request(url, fd, callback)
                            .then(res => {
                                layer.closeAll()
                                if (res.status == 0) {
                                    layer.msg(res.mes)
                                } else if (res.object.status === 200) {
                                    callback(null, res.object)
                                } else if (res.object.status === 500) {
                                    layer.msg(res.object.msg)
                                }
                            })
                    }).catch(error => {
                        layer.closeAll()
                        callback(error, null)
                    }).finally(() => {
                    });
                }
            })(i)
        }
    }

php 部分

class PartUploadLogic
{
    protected $blobNum;
    protected $totalBlobNum;

    public function UploadInChunks()
    {
        set_time_limit(600);
        ini_set('memory_limit', '3000M');
        $file = $_FILES['file'];
        $this->blobNum = I('index/d', 1);
        $this->totalBlobNum = I('total/d', 1);
        $fileName = I("fileName/s");
        $endPath = '/uploads/file';
        $tmpPath = $endPath . 'tmp/';

        $savePath = date("Ymd");
        $tmpRelativePath = "$tmpPath{$savePath}";
        $tmpSavePath = realpath('') . $tmpRelativePath . "/$fileName";

        if (!is_dir($tmpSavePath)) {
            mkdir($tmpSavePath, 755, true);
        }

        $saveName = $fileName . '_' . $this->blobNum;
        move_uploaded_file($file['tmp_name'], "$tmpSavePath/$saveName");

        // 合并上传的子文件
        if ($this->totalBlobNum == $this->blobNum) {
            if ($this->totalBlobNum > 1) {
                sleep(2);
            }
            try {
                $endRelativePath = "$endPath/$savePath";
                $finishSavePath = realpath('') . $endRelativePath;
                if (!is_dir($finishSavePath)) {
                    mkdir($finishSavePath, 755, true);
                }
                $fp = fopen("{$finishSavePath}/{$fileName}.pdf", "w+");

                // 合并文件
                for ($i = 1; $i <= $this->totalBlobNum; $i++) {
                    $value = "$tmpSavePath/{$fileName}_{$i}";
                    $handle = fopen($value, "rb");
                    if (empty($handle)) {
                        failData('读取不到分包数据');
                    }
                    fwrite($fp, fread($handle, filesize($value)));
                    fclose($handle);
                    unset($handle);
                    @unlink($value);
                }
                del_dir($tmpSavePath);

            } catch (\Exception $exception) {
                return ['status' => 500, 'msg' => "文件合并失败{$exception->getLine()}{$exception->getMessage()}}"];
            }
            return ['status' => 200, 'msg' => 'success', 'path' => "$endRelativePath/{$fileName}.pdf"];
        }
        return ['status' => 201, 'msg' => 'success', 'fileName' => $fileName];
    }
}
分片上传
Theme Jasmine by Kent Liao