php中上传大文件的方法

19 篇文章 1 订阅
介绍:在项目开发的过程中有时候会用到上传大文件或者视频的方法,下面介绍两种大文件上传的方法。
1.Thinkphp5框架中采用前后端不分离的同源模式,前端使用 webuploader 分片上传的方法,后端使用分片上传。
    /**
     * 大文件上传
     * @return \think\response\Json
     */
    public function maxFileUpload()
    {
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
        header("Cache-Control: no-store, no-cache, must-revalidate");
        header("Cache-Control: post-check=0, pre-check=0", false);
        header("Pragma: no-cache");
        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            json(['code' => -1, 'msg' => '使用get或post方法上传']) -> send();
            exit;
        }
        if (!empty($_REQUEST['debug'])) {
            $random = rand(0, intval($_REQUEST['debug']));
            if ($random === 0) {
                json(['code' => -1, 'msg' => '上传失败']) -> send();
                exit;
            }
        }
        //设置过期时间
        @set_time_limit(3600);
        $path = './uploads/max_file';//文件路径
        $targetDir = $path . '\\' . 'file_tmp';
        $uploadDir = $path;
        $cleanupTargetDir = true;
        $maxFileAge = 20 * 3600;
        if (!is_dir($targetDir)) {
            mkdir($targetDir, 0777, true);
        }
        if (!is_dir($uploadDir)) {
            mkdir($uploadDir, 0777, true);
        }
        if (isset($_REQUEST['name'])) {
            $fileName = $_REQUEST['name'];
        } else if (!empty($_FILES)) {
            $fileName = $_FILES['file']['name'];
        } else {
            $fileName = uniqid('file_');
        }
        //原始上传的文件名
        $oldName = $fileName;
        $change_file_info = pathinfo($fileName);
        //文件后缀
        $extension = $change_file_info['extension'];
        //编码过的文件名
        $fileName = md5(basename($fileName, '.' . $extension));
        $filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName . '.' . $extension;
        $chunk = isset($_REQUEST['chunk']) ? intval($_REQUEST['chunk']) : 0;
        $chunks = isset($_REQUEST['chunks']) ? intval($_REQUEST['chunks']) : 1;
        if ($cleanupTargetDir) {
            if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
                json(['code' => -1, 'msg' => '上传失败']) -> send();
                exit;
            }
            while (($file = readdir($dir)) !== false) {
                $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
                if ($tmpfilePath == "{$filePath}_{$chunk}.part" || $tmpfilePath == "{$filePath}_{$chunk}.parttmp") {
                    continue;
                }
                if (preg_match('/\.(part|parttmp)$/', $file) && (@filemtime($tmpfilePath) < time() - $maxFileAge)) {
                    @unlink($tmpfilePath);
                }
            }
            closedir($dir);
        }
        if (!$out = @fopen("{$filePath}_{$chunk}.parttmp", "wb")) {
            json(['code' => -1, 'msg' => '上传失败']) -> send();
            exit;
        }
        if (!empty($_FILES)) {
            if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
                json(['code' => -1, 'msg' => '上传失败']) -> send();
                exit;
            }
            if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
                json(['code' => -1, 'msg' => '上传失败']) -> send();
                exit;
            }
        } else {
            if (!$in = @fopen("php://input", "rb")) {
                json(['code' => -1, 'msg' => '上传失败']) -> send();
                exit;
            }
        }
        while ($buff = fread($in, 4096)) {
            fwrite($out, $buff);
        }
        @fclose($out);
        @fclose($in);
        rename("{$filePath}_{$chunk}.parttmp", "{$filePath}_{$chunk}.part");
        $done = true;
        for ($index = 0; $index < $chunks; $index++) {
            if (!file_exists("{$filePath}_{$index}.part")) {
                $done = false;
                break;
            }
        }
        if ($done) {
            $uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName . "." . $extension;
            if (!$out = @fopen($uploadPath, "wb")) {
                json(['code' => -1, 'msg' => '上传失败']) -> send();
                exit;
            }
            if (flock($out, LOCK_EX)) {
                for ($index = 0; $index < $chunks; $index++) {
                    if (!$in = @fopen("{$filePath}_{$index}.part", "rb")) {
                        break;
                    }
                    while ($buff = fread($in, 4096)) {
                        fwrite($out, $buff);
                    }
                    @fclose($in);
                    @unlink("{$filePath}_{$index}.part");
                }
                flock($out, LOCK_UN);
            }
            @fclose($out);
            $response = [
                'success' => true,
                'oldName' => $oldName,//上传文件的原始文件名和后缀
                "newName" => $fileName . "." . $extension,//编码文件名和后缀
                'fileSuffixes' => $extension,//文件格式
                'path' => $path//文件路径
            ];
            //执行成功以后删除临时文件夹
            @rmdir($targetDir);
            return json($response);
        } else {
            json(['code' => 1, 'msg' => '分片上传成功']) -> send();
        }
    }
2.前后端分离的方式,前端采用 react,后端采用 Thinkphp5。
    /**
     * 大文件上传
     * @return \think\response\Json
     */
    public function maxFileUpload()
    {
        //设置过期时间
        @set_time_limit(3600);
        //跨域发送请求
        header_public();
        //头文件定义属性
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
        header("Cache-Control: no-store, no-cache, must-revalidate");
        header("Cache-Control: post-check=0, pre-check=0", false);
        header("Pragma: no-cache");
        if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            exit;
        }
        $param = input('param.');
        $chunk = $param['chunk'] ?? 0;//分片号
        $chunk_count = $param['chunk_count'] ?? 1;//总分片数
        $file_name = $param['file_name'] ?? '';//原文件名称
        $file_temp_name = $param['file_temp_name'] ?? '';//切片文件名称

        $path = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'max_file';//文件路径
        $date_dir = date('Ymd');
        $targetDir = $path . DS . 'patrol_tmp' . DS;//无法并发生成文件夹,跨越天数也有bug,改为同一目录下不再按日期生成目录
        $uploadDir = $path . DS . 'patrol' . DS . $date_dir;
        $this->mkdir($targetDir);
        $this->mkdir($uploadDir);
        //上传完毕
        if ($chunk !== $chunk_count) {
            $file = request()->file('file');
            $file->move($targetDir, "{$file_temp_name}.{$chunk}");//改名移动到临时目录
            return json([
                'code' => 1,
                'done' => false,
                'msg' => "切片{$chunk}上传成功",
            ]);
        }
        $fileName_arr = pathinfo($file_name);
        $finalFileName = $fileName_arr['filename'] . date("YmdHis") . "." . $fileName_arr['extension'];//最终存储的文件名
        $uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $finalFileName;
        $out = @fopen($uploadPath, "wb");
        if (flock($out, LOCK_EX)) {
            for ($index = 0; $index < $chunk_count; $index++) {
                $tmp_file = "{$targetDir}{$file_temp_name}.{$index}";
                if (!$in = @fopen($tmp_file, "rb")) {
                    return json([
                        'code' => -1,
                        'done' => false,
                        'msg' => "切片{$index}号,没有上传",
                    ]);
                }
                while ($buff = fread($in, 4096)) {
                    fwrite($out, $buff);
                }
                @fclose($in);
                @unlink($tmp_file);
            }
            flock($out, LOCK_UN);
        }
        @fclose($out);
        $this->deleteOldFile($targetDir);
        //获取原始的文件名
        $pathInfo = pathinfo($finalFileName);
        $new_oldName = $pathInfo["filename"];

        //保存到附件表,并返回附件id
        $filepath = str_replace(ROOT_PATH . 'public', '', $uploadPath);
        $response = [
            'code' => 1,
            'done' => true,
            'oldName' => $new_oldName,
            'filePath' => $filepath,
            'src' => $filepath,
            'fileSuffixes' => $pathInfo['extension'],
	        'data'=>$data,
        ];
        return json($response);
    }
3.附上封装的公用的 header 头跨域方法。
//公用的header头方法,需要添加允许x-requested-with请求头,需要添加header头里的参数可以继续添加
if (!function_exists('header_public')) {
    function header_public($headers = 'X-Requested-With,key,token,content-type')
    {
        ob_clean();
        header('Access-Control-Allow-Origin:*');
        header('Access-Control-Allow-Methods:OPTIONS, GET, POST');//允许option,get,post请求
        header("Access-Control-Allow-Headers:{$headers}");//允许x-requested-with请求头,需要添加header头里的参数可以继续添加
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值