高效实现大文件分片上传与断点续传:从 PHP 到阿里云 OSS

引言

在处理大规模数据传输,尤其是视频、大型文档等超大文件时,传统的单次HTTP请求方式容易遇到诸多问题,如请求大小限制、超时以及传输中断。为了解决这些问题,采用大文件分片上传和断点续传技术成为了一种有效的方法。本文将详细介绍如何使用PHP结合阿里云OSS服务来实现这一功能,并提供完整的代码示例。

一、原理概览

大文件分片上传的原理是将一个大的文件分割成若干个小块(或称为“片段”),然后分别上传这些小块。这样做的好处包括但不限于:

  • 减少失败风险:如果某个片段上传失败,只需重新上传该片段而非整个文件。
  • 提高效率:多个片段可以并行上传,加速整体传输过程。
  • 优化资源利用:通过控制每个片段的大小,可以更好地适应网络状况及服务器配置限制。

二、环境配置

Nginx & PHP设置

为了支持大文件上传,首先需要调整Nginx和PHP的相关配置以允许更大的请求体大小。以下是一个基本的配置例子:

# Nginx配置
client_max_body_size 2G;
; PHP配置
upload_max_filesize = 2G
post_max_size = 2G

请根据实际情况调整具体数值。

前端准备

前端方面推荐使用WebUploader库进行文件选择与上传进度管理。它提供了强大的功能支持,比如拖拽、预览以及多文件并发上传等特性。

HTML结构

<div class="layui-input-block layui-upload">
    <button type="button" class="layui-btn layui-btn-normal" id="chooseList">选择文件</button>
    <div class="layui-upload-list">
        <table class="layui-table">
            <thead>
                <tr>
                    <th>文件名</th>
                    <th>大小</th>
                    <th>上传进度</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody id="uploadList"></tbody>
        </table>
    </div>
</div>
<script src="__STATIC__/admin/js/sparkmd5/sparkmd5.js?v={$version}"></script>

JavaScript逻辑

layui.use(['form', 'upload', 'element'], function() {
    var form = layui.form,
        upload = layui.upload,
        element = layui.element,
        $ = layui.$;

    upload.render({
        elem: '#chooseList',
        url: '../Ajax/multiupload',
        accept: 'file',
        auto: false,
        choose: function(obj) {
            // 文件选择后的处理逻辑...
        },
        done: function(res) {
            // 分片上传成功后回调
        },
        error: function(index) {
            // 出现错误时回调
        }
    });

    // 分片上传函数
    function multiupload(progressTimer, data, file, partSize, key, fileName, fileExt, hash_name, obj) {
        // 实现细节...
    }

    // 计算文件MD5值
    function getmd5(file, chunkSize) {
        return new Promise((resolve, reject) => {
            let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
            let chunks = Math.ceil(file.size / chunkSize);
            let currentChunk = 0;
            let spark = new SparkMD5.ArrayBuffer();
            let fileReader = new FileReader();

            fileReader.onload = function(e) {
                spark.append(e.target.result);
                currentChunk++;
                if (currentChunk < chunks) {
                    loadNext();
                } else {
                    resolve(spark.end());
                }
            };

            fileReader.onerror = function(e) {
                reject(e);
            };

            function loadNext() {
                let start = currentChunk * chunkSize;
                let end = start + chunkSize;
                if (end > file.size) {
                    end = file.size;
                }
                fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
            }

            loadNext();
        });
    }
});

后端实现

后端主要负责接收前端发送过来的分片数据,并调用阿里云OSS API完成实际的数据存储工作。这里我们自定义了一个名为multiupload的方法来替代原有的multiuploadFile方法,以便更好地适配我们的需求。

public function multiupload($bucket, $object, $file, $options = null)
{
    $this->precheckCommon($bucket, $object, $options);

    if (empty($file)) {
        throw new OssException("parameter invalid, file is empty");
    }

    $uploadFile = OssUtil::encodePath($file);
    if (!isset($options[self::OSS_CONTENT_TYPE])) {
        $options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object, $uploadFile);
    }

    if (!$options['upload_id']) {
        $options['upload_id'] = $this->initiateMultipartUpload($bucket, $object, [
            'Content-Type' => $options[self::OSS_CONTENT_TYPE],
            'partSize' => $options['part_size']
        ]);
    }

    $up_options = [
        self::OSS_FILE_UPLOAD => $uploadFile,
        self::OSS_PART_NUM => $options['part_num'],
        self::OSS_SEEK_TO => 0,
        self::OSS_LENGTH => $options['part_size'],
        self::OSS_CHECK_MD5 => false,
    ];

    $response_upload_part = $this->uploadPart($bucket, $object, $options['upload_id'], $up_options);

    if (isset($options['etag'])) {
        $options['etag'][] = substr($response_upload_part, 1, -1);
    } else {
        $options['etag'] = substr($response_upload_part, 1, -1);
    }

    if ($options['status'] == 2) {
        $uploadParts = [];
        foreach ($options['etag'] as $i => $value) {
            $uploadParts[] = [
                'PartNumber' => ($i + 1),
                'ETag' => $value
            ];
        }

        $completer = $this->completeMultipartUpload($bucket, $object, $options['upload_id'], $uploadParts);
        $options['url'] = $completer['info']['url'];
    }

    return $options;
}

以上就是如何使用PHP配合阿里云OSS实现高效的大文件分片上传解决方案。希望这篇指南能够帮助您构建更加稳定可靠的文件传输系统!

本文来源于互联网,著作权归作者 [  ] 享有,本文仅供个人学习、研究和欣赏使用,如有异议,请联系站务及时处理。

▋ 发表留言

?