PHP实现大文件分片上传的完整代码

 更新时间:2026年06月17日 08:22:31   作者:JSON_L  
在日常开发中,直接使用PHP原生上传接口传输几百MB甚至几GB的大文件,会遇到诸多致命问题,分片上传是解决以上问题的最优方案,本文将从零实现一套 前端分片切割 + PHP后端接收合并 的完整上传方案,代码简洁、可直接部署使用,需要的朋友可以参考下

实战干货:原生PHP实现大文件分片上传,无需框架、开箱即用,支持普通大文件上传,完美解决PHP大文件上传超时、内存溢出、表单限制等核心问题,适配服务器生产环境。

在日常开发中,直接使用PHP原生上传接口传输几百MB甚至几GB的大文件,会遇到诸多致命问题:

  • PHP脚本执行超时(max_execution_time 限制)
  • 服务器POST数据大小、单文件上传大小限制
  • 一次性读取大文件导致内存溢出
  • 上传中断需要重新上传,不支持断点续传

分片上传是解决以上问题的最优方案,本文将从零实现一套 前端分片切割 + PHP后端接收合并 的完整上传方案,代码简洁、可直接部署使用。

一、分片上传核心原理

分片上传的核心思想:化整为零,分批传输,最后合并,具体流程如下:

  1. 前端切片:通过JS File API 将超大文件切割为固定大小的小分片(1MB~5MB)
  2. 唯一标识:对原文件生成MD5唯一指纹,区分不同文件的分片,避免冲突
  3. 逐片上传:异步批量上传每一个分片,携带分片索引、总分片数等参数
  4. 后端暂存:PHP接收分片,临时存储在单独目录,按规则命名区分
  5. 合并文件:所有分片上传完成后,后端按索引顺序合并所有分片,生成完整文件
  6. 清理缓存:合并完成后自动删除临时分片文件,释放服务器存储空间

二、环境前置配置(必改)

PHP默认配置严格限制文件上传,必须修改 php.ini 配置文件,否则分片上传会失效。

修改 php.ini 核心参数

; 单文件最大上传大小(根据业务调整)
upload_max_filesize = 100M
; POST请求最大数据大小
post_max_size = 100M
; 脚本执行时间 0=不限制
max_execution_time = 0
; 脚本内存限制,大文件上传建议调高
memory_limit = 512M
; 关闭文件上传临时文件清理(可选)
upload_tmp_dir = /tmp

修改完成后,重启Apache/Nginx、PHP-FPM 服务生效。

三、完整代码实现

整套方案分为 前端上传页面(index.html) 和 PHP后端接收接口(upload.php),两个文件放在同一目录即可运行。

前端页面:文件切片+异步上传

实现文件选择、自动切片、进度条展示、逐片异步上传功能,原生JS无依赖。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>PHP大文件分片上传</title>
    <style>
        .box{margin: 50px auto;width: 400px;}
        .progress{width: 100%;height: 12px;background: #eee;border-radius: 6px;margin: 15px 0;overflow: hidden;}
        .progress-bar{height: 100%;background: #1677ff;width: 0;transition: width 0.3s;}
        .tip{color: #666;margin-top: 10px;}
    </style>
</head>
<body>
<div class="box">
    <h3>大文件分片上传演示</h3>
    <input type="file" id="file" />
    <div class="progress"><div class="progress-bar" id="progress"></div></div>
    <div class="tip" id="msg">请选择需要上传的大文件</div>
</div>
<script>
    // 单个分片大小:1MB(可调整2M/5M)
    const CHUNK_SIZE = 1 * 1024 * 1024;
    const fileDom = document.getElementById('file');
    const progressDom = document.getElementById('progress');
    const msgDom = document.getElementById('msg');
    // 监听文件选择
    fileDom.addEventListener('change', async function (e) {
        const file = e.target.files[0];
        if (!file) return;
        // 初始化状态
        progressDom.style.width = '0%';
        msgDom.innerText = '正在解析文件...';
        // 生成文件唯一标识(简易MD5,生产建议使用spark-md5)
        const fileMd5 = await getFileUniqueId(file);
        // 计算总分片数
        const totalChunk = Math.ceil(file.size / CHUNK_SIZE);
        let successCount = 0;
        msgDom.innerText = `开始上传,共${totalChunk}个分片`;
        // 循环上传所有分片
        for (let i = 0; i < totalChunk; i++) {
            // 切割文件分片
            const start = i * CHUNK_SIZE;
            const end = Math.min(start + CHUNK_SIZE, file.size);
            const chunk = file.slice(start, end);
            // 组装上传参数
            const formData = new FormData();
            formData.append('chunk', chunk);         // 分片文件
            formData.append('index', i);             // 当前分片索引
            formData.append('total', totalChunk);    // 总分片数
            formData.append('md5', fileMd5);         // 文件唯一标识
            formData.append('filename', file.name);  // 原始文件名
            // 上传当前分片
            const res = await uploadChunk(formData);
            if (res.code !== 0) {
                msgDom.innerText = `第${i+1}片上传失败,请重试`;
                return;
            }
            // 更新进度
            successCount++;
            const percent = (successCount / totalChunk) * 100;
            progressDom.style.width = percent + '%';
            msgDom.innerText = `上传中 ${successCount}/${totalChunk}`;
        }
        msgDom.innerText = '✅ 文件上传完成!';
    });
    // 分片上传请求
    function uploadChunk(data) {
        return new Promise(resolve => {
            const xhr = new XMLHttpRequest();
            xhr.open('POST', 'upload.php', true);
            xhr.onload = function () {
                resolve(JSON.parse(xhr.responseText));
            };
            xhr.onerror = function () {
                resolve({code: 1, msg: '请求异常'});
            };
            xhr.send(data);
        });
    }
    // 生成文件唯一标识(简易版)
    function getFileUniqueId(file) {
        return new Promise(resolve => {
            const reader = new FileReader();
            reader.readAsArrayBuffer(file);
            reader.onload = function (e) {
                // 简易唯一ID,生产环境替换为完整MD5
                resolve(btoa(e.target.result.slice(0, 100)));
            };
        });
    }
</script>
</body>
</html>

后端接口:接收分片+合并文件

PHP核心接口,实现分片接收、临时存储、自动合并、缓存清理全逻辑。

<?php
// 设置响应头
header('Content-Type: application/json; charset=utf-8');
// 基础配置
$config = [
    'upload_path' => './uploads/',  // 最终文件保存目录
    'chunk_path'  => './chunks/',   // 分片临时存储目录
];
// 初始化目录
!is_dir($config['upload_path']) && mkdir($config['upload_path'], 0777, true);
!is_dir($config['chunk_path'])  && mkdir($config['chunk_path'], 0777, true);
// 接收前端参数
$index    = intval($_POST['index']);     // 当前分片索引
$total    = intval($_POST['total']);     // 总分片数
$file_md5 = trim($_POST['md5']);         // 文件唯一标识
$filename = trim($_POST['filename']);    // 原始文件名
$chunk    = $_FILES['chunk'];            // 分片文件
// 分片保存路径:md5_索引 命名,防止文件冲突
$chunk_file = $config['chunk_path'] . $file_md5 . '_' . $index;
// 移动分片文件到临时目录
if (!move_uploaded_file($chunk['tmp_name'], $chunk_file)) {
    exit(json_encode(['code' => 1, 'msg' => '分片上传失败']));
}
// 所有分片上传完毕,执行文件合并
if ($index == $total - 1) {
    // 拼接最终文件路径
    $final_file = $config['upload_path'] . $filename;
    // 二进制方式创建文件
    $handle = fopen($final_file, 'wb');
    // 按顺序读取分片并写入最终文件
    for ($i = 0; $i < $total; $i++) {
        $temp_chunk = $config['chunk_path'] . $file_md5 . '_' . $i;
        // 写入分片内容
        fwrite($handle, file_get_contents($temp_chunk));
        // 删除已合并的分片
        unlink($temp_chunk);
    }
    fclose($handle);
}
// 返回成功响应
echo json_encode(['code' => 0, 'msg' => 'success']);
?>

四、项目目录结构

部署后自动生成目录,无需手动创建,结构清晰:

项目根目录
├── index.html      # 前端上传页面
├── upload.php      # 后端接收合并接口
├── uploads/        # 最终上传文件存储目录(自动生成)
└── chunks/         # 分片临时缓存目录(自动生成)

五、核心知识点解析

分片大小选择

  • 推荐区间:1MB - 5MB
  • 分片过小:请求次数过多,占用服务器HTTP资源,上传变慢
  • 分片过大:单请求超时概率高,失去分片意义

分片命名规则

采用 文件MD5_分片索引 命名,完美解决:

  • 多文件同时上传的分片冲突问题
  • 分片顺序错乱导致文件损坏问题

二进制合并原理

使用 fopen(xx,wb) 二进制覆写模式,逐片拼接文件二进制流,不改变文件编码、格式,支持视频、压缩包、图片、文档等所有文件格式。

六、生产环境进阶优化(必备)

上述代码为基础可用版本,生产环境需优化以下功能,提升稳定性和安全性:

实现断点续传

新增 查询已上传分片接口,上传前请求后端,获取当前文件已上传的分片索引,跳过已上传分片,中断后无需重传全部文件。

完善安全校验

  • 限制上传文件后缀,禁止php、exe、sh等恶意文件
  • 文件合并后校验整体MD5,防止文件损坏、篡改
  • 增加请求token验证,防止恶意刷接口

异常容错处理

  • 前端增加分片失败自动重试机制
  • 后端增加分片过期清理脚本,定时删除残留缓存分片
  • 增加文件大小、分片数量合法性校验

性能优化

  • 前端开启并发上传(同时上传2-3个分片),提升上传速度
  • 后端使用文件指针流式合并,替代file_get_contents,降低大文件内存占用

七、常见问题避坑指南

  • 上传超时:务必设置 max_execution_time=0,关闭脚本执行时间限制
  • 文件合并损坏:必须按分片索引顺序合并,不可无序拼接
  • 权限报错:给项目目录设置 0777 权限,保证PHP有读写、创建、删除权限
  • 分片残留:服务器定时清理 chunks 临时目录,避免磁盘占用过高
  • 大文件内存溢出:调高 memory_limit,优先使用流式读写

八、总结

PHP大文件分片上传的核心逻辑就是 前端切片、后端暂存、最终合并。本文提供的源码无框架依赖、轻量化、可直接部署,彻底解决了传统PHP单文件上传的各类限制问题。

基础版本可满足中小型项目需求,搭配断点续传、安全校验、定时清理等进阶功能后,可直接用于企业级生产环境。

以上就是PHP实现大文件分片上传的完整代码的详细内容,更多关于PHP大文件分片上传的资料请关注脚本之家其它相关文章!

相关文章

  • php获得url参数中具有&的值的方法

    php获得url参数中具有&的值的方法

    经常会遇到要获取上一页地址的路径。你可以返回上一页使用,实现这个是通过JS来操作的,下面有个不错的示例,大家可以参考下
    2014-03-03
  • 详解PHP错误日志的获取方法

    详解PHP错误日志的获取方法

    在php中我们会经常碰到一些错误要去处理,下面我来总结php中的错误报告及日志的获取方法等方面的内容做一些总结,需要的朋友可以参考下。
    2015-07-07
  • php 引用(&)详解

    php 引用(&)详解

    php的引用(就是在变量或者函数、对象等前面加上&符号)
    2009-11-11
  • php的字符串用法小结

    php的字符串用法小结

    php的字符串用法小结,刚开始接触php字符串的朋友可以参考下。
    2010-06-06
  • PHP优化教程之解决嵌套问题

    PHP优化教程之解决嵌套问题

    这篇文章主要给大家介绍了关于PHP优化教程之解决嵌套问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • PHP编程快速实现数组去重的方法详解

    PHP编程快速实现数组去重的方法详解

    这篇文章主要介绍了PHP编程快速实现数组去重的方法,结合实例形式分析了php数组去重复函数与使用技巧,需要的朋友可以参考下
    2017-07-07
  • 域名和cookie问题(域名后缀)

    域名和cookie问题(域名后缀)

    偶然想到一个问题:www.g.cn能把cookie设置为.g.cn,那么www.com.cn能设置把cookie设置为.com.cn吗
    2012-10-10
  • php模拟登陆的实现方法分析

    php模拟登陆的实现方法分析

    这篇文章主要介绍了php模拟登陆的实现方法,实例分析了snoopy与curl两种实现方法,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-01-01
  • PHP文件上传原理简单分析

    PHP文件上传原理简单分析

    PHP文件上传原理简单分析,学习php的朋友可以参考下,确实比asp的要简单很多。
    2011-05-05
  • PHP冒泡排序算法代码详细解读

    PHP冒泡排序算法代码详细解读

    PHP冒泡排序算法代码详细解读,需要学习php冒泡排序算法的朋友可以参考下。
    2011-07-07

最新评论