大文件传输解决方案 : 分片上传

不少项目中会遇到上传视频等大文件的业务场景;
此类业务共同点 : 传输慢 , 占用带宽;
由此就需要用另一种解决方法 分片上传;
带大家用php实现一下;
本次用到的是php框架laravel;
各种语言和框架同时适用;
语言和实现的思路是一样的;
使用laravel 还可以使用这个扩展包(AetherUpload-Laravel);
有条件的可以使用第三方七牛云,阿里云等大公司分片上传服务;

分片上传原理 :

  1. 将上传的文件按照一定规则进行分割 , 例如 : 每一片不超过1M;
  2. 按照一定的策略(同步或异步)发送各个分片数据块;
  3. 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件;

服务端接收上传的文件片,并判断当前文件夹下的文件数据是否等于客户端分片数,如果是就合并文件,删除上传的文件块

  /**
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     * @author zhouwang
     * @description 分片上传
     */
    public function sliceUploadMedia(Request $request)
    {
        try {
            set_time_limit(0);
            ini_set('memory_limit', '800M');

            // 文件流
            $file = $request->file('file');
            // 当前是第几片
            $blob_num = $request->post('chunkNumber', 1);
            // 总共多少片
            $total_blob_num = $request->post('totalChunks', 1);
            // 文件名称不带文件后缀
            $file_name = $request->post('filename', '');
            // 上传文件唯一标识
            $identifier = $request->post('identifier', '');
            // 文件大小
            $size = $request->post('totalSize', '');
            // 获取路径信息
            $pathInfo = pathinfo($file_name);
            // 获取文件扩展名称
            $extension = $pathInfo['extension'] ?? '';
            // 获取文件名称
            $file_name = $pathInfo['filename'] ?? '';
            // 获取文件MD5,时间戳,时长
            list($signature, $time, $video_second, $extend_name) = explode('-', $identifier);

            // 校验参数
            if ($blob_num <= 0)
                return self::fail('请上传正确的当前片数');

            if ($total_blob_num <= 0)
                return self::fail('请上传分片总片数');

            if (empty($file_name))
                return self::fail('无效的文件名称');

            if (empty($extension))
                return self::fail('无效的文件后缀');

            // 下载文件的本地
            $directory = env('MATERIAL_ROOT') . "/file/$signature/$time/";

            // 是否存在该目录 , 不存在创建目录
            if (!is_dir($directory))
                mkdir($directory, 0775, true);

            // 判断文件在上传过程中是否出错
            if (!$file->isValid())
                return self::fail('上传文件错误');

            // 拼接文件名称 + 文件第几片
            $file_name_path = $file_name . '_' . $blob_num;

            // 年月目录
            $savePath = date('Ym') . '/' . date('d');

            // 移动文件目录
            $saveDir = getenv('MATERIAL_ROOT') . "/{$savePath}/";

            // 是否存在该目录 , 不存在创建目录
            if (!is_dir($saveDir))
                mkdir($saveDir, 0777, true);

            // 判断是否存在文件名称
            $path = env('MATERIAL_ROOT') . "/{$savePath}/" . $file_name . '.' . $extend_name;

            // 移动文件
            $result = move_uploaded_file($file->getRealPath(), $directory . $file_name_path);

            // 并发请求判断文件夹下面的文件数量是否等于分片数量
            if (getFileNumber($directory) == $total_blob_num) {

                $dirName = $directory . $file_name;

                // 循环合并
                for ($i = 1; $i <= $total_blob_num; $i++) {
                    // 读取文件流
                    $blob = file_get_contents($dirName . '_' . $i);
                    // 文件合并
                    file_put_contents($path, $blob, FILE_APPEND);
                }

                // 删除文件
                for($i=1;$i<=$total_blob_num;$i++){
                    // 合并完删除文件块
                    @unlink($directory . $file_name . '_' .$i);
                }

                // 远程路径
                $url = strtr($path, [env('MATERIAL_ROOT') => env('MATERIAL_URL')]);

                // 返回成功
                return self::success(compact('size', 'extend_name', 'path', 'video_second', 'width', 'height', 'file_name', 'url', 'signature'));
            }

            // 判断返回值
            if ($result)
                return self::success([], '上传成功');

            return self::fail('上传失败');

        } catch (\Exception $exception) {

            return self::fail('服务器正在开小差');
        }
    }

至此关于分片上传简单实现Demo已经说完;
大致应该了解怎么实现分片上传了吧;
希望对大家有帮助;
因为大文件上传现实中经常遇到的事情;

史大坨博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论