大文件上传,对接阿里oss采用前端分片技术。完成对应需求!

news2025/5/25 8:42:48

最近做了一个大文件分片上传的功能,记录下

1. 首先是安装阿里云 oss 扩展

composer require aliyuncs/oss-sdk-php

去阿里云 oss 获取配置文件

AccessKey ID = ***
AccessKey Secret = ***
Bucket名称 = ***
Endpoint = ***

2. 前端上传,对文件进行分片

<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">

    <div class="form-group">
        <label class="control-label col-xs-12 col-sm-2">{:__('选择本地文件')}:</label>
        <div class="col-xs-12 col-sm-8">
            <input type="file" id="fileInput">
            <div>
                <a href="#" onclick="startUpload()"><i class="fa fa-upload"></i>选择完点击上传(请等待上传完成)</a>
            </div>
            <div id="progress" style="margin-top:10px;"></div>
        </div>
    </div>
    <div class="form-group layer-footer">
        <label class="control-label col-xs-12 col-sm-2"></label>
        <div class="col-xs-12 col-sm-8">
            <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
        </div>
    </div>
</form>

<script>
    let chunkSize = 5 * 1024 * 1024; // 分片大小5MB
    let uploadId = '';
    let objectName = '';
    let parts = [];

    const CHUNK_SIZE = 5 * 1024 * 1024; // 分片阈值5MB

    async function startUpload() {
        const file = document.getElementById('fileInput').files[0];
        if (!file) {
            layer.msg('请选择文件', {icon: 1});
        }

        // 根据文件大小选择上传方式
        if (file.size <= CHUNK_SIZE) {
            await directUpload(file);
        } else {
            await chunkedUpload(file);
        }
    }

    // 开始上传
    async function directUpload(file) {
        const formData = new FormData();
        formData.append('file', file);
        formData.append('file_name', file.name);

        // 显示进度条
        const progressBar = document.getElementById('progress');
        progressBar.innerHTML = '上传进度:0%';

        try {
            const res = await fetch('/api/directUpload', {
                method: 'POST',
                body: formData,
            });

            const data = await res.json();
            if (data.code === 1) {
                progressBar.innerHTML = '上传进度:100%';
                $("#c-name").val(data.name);
                $("#c-fullurl").val(data.fullurl);
                layer.msg('上传成功', {icon: 1});
            } else {
                throw new Error(data.msg);
            }
        } catch (error) {
            progressBar.innerHTML = '上传失败';
            console.error('直接上传失败:', error);
        }
    }

    async function chunkedUpload(file) {
        const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
        let uploadedChunks = 0;

        // 初始化分片上传
        const initRes = await fetch('api/initUpload', {
            method: 'POST',
            body: JSON.stringify({filename: file.name}),
            headers: {'Content-Type': 'application/json'}
        });
        const initData = await initRes.json();
        if (initData.code !== 1) return alert('初始化失败');

        const {uploadId, objectName} = initData;
        const parts = [];

        // 上传所有分片
        for (let i = 0; i < totalChunks; 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('part', chunk);
            formData.append('uploadId', uploadId);
            formData.append('objectName', objectName);
            formData.append('partNumber', i + 1);

            const uploadRes = await fetch('api/uploadPart', {
                method: 'POST',
                body: formData
            });
            const partData = await uploadRes.json();

            if (partData.code === 1) {
                parts.push({
                    PartNumber: partData.partNumber,
                    ETag: partData.etag
                });
                uploadedChunks++;

                // 更新进度
                const progress = (uploadedChunks / totalChunks * 100).toFixed(2);
                document.getElementById('progress').innerHTML = `上传进度:${progress}%`;
            }
        }

        // 合并分片
        const completeRes = await fetch('api/completeUpload', {
            method: 'POST',
            body: JSON.stringify({
                uploadId,
                objectName,
                parts: JSON.stringify(parts)
            }),
            headers: {'Content-Type': 'application/json'}
        });
        const completeData = await completeRes.json();

        if (completeData.code === 1) {
            $("#c-name").val(completeData.name);
            $("#c-fullurl").val(completeData.fullurl);
            layer.msg('上传成功', {icon: 1});
        } else {
            layer.msg('上传失败' + completeData.msg, {icon: 2});
        }
    }
</script>

2. 后端控制器

<?php

namespace app\****;

use OSS\Core\OssException;
use OSS\OssClient;

class Attachment
{
    // 初始化分片上传
    public function initUpload()
    {
        $object = 'uploads/' . date('Ymd') . '/' . $this->request->post('filename');
        try {
            $ossClient = new OssClient(
                config('alioss.accessKeyId'),
                config('alioss.accessKeySecret'),
                config('alioss.endpoint')
            );
            $uploadId = $ossClient->initiateMultipartUpload(config('alioss.bucket'), $object);
            return json([
                'code' => 1,
                'uploadId' => $uploadId,
                'objectName' => $object
            ]);
        } catch (OssException $e) {
            return json(['code' => 0, 'msg' => $e->getMessage()]);
        }
    }

    // 上传分片
    public function uploadPart()
    {
        $data = $this->request->post();
        try {
            $ossClient = new OssClient(
                config('alioss.accessKeyId'),
                config('alioss.accessKeySecret'),
                config('alioss.endpoint')
            );
            $options = [
                OssClient::OSS_FILE_UPLOAD => $_FILES['part']['tmp_name'],
                OssClient::OSS_PART_NUM => $data['partNumber'],
                OssClient::OSS_CHECK_MD5 => true
            ];
            $etag = $ossClient->uploadPart(
                config('alioss.bucket'),
                $data['objectName'],
                $data['uploadId'],
                $options
            );
            return json([
                'code' => 1,
                'etag' => $etag,
                'partNumber' => $data['partNumber']
            ]);
        } catch (OssException $e) {
            return json(['code' => 0, 'msg' => $e->getMessage()]);
        }
    }

    // 完成上传
    public function completeUpload()
    {
        $data = $this->request->post();
        try {
            $ossClient = new OssClient(
                config('alioss.accessKeyId'),
                config('alioss.accessKeySecret'),
                config('alioss.endpoint')
            );
            $result = $ossClient->completeMultipartUpload(
                config('alioss.bucket'),
                $data['objectName'],
                $data['uploadId'],
                json_decode($data['parts'], true)
            );
            return json([
                'code' => 1,
                'url' => $result['oss-request-url'],
                'name' => pathinfo($data['objectName'], PATHINFO_FILENAME),
                'fullurl' => strstr($result['oss-request-url'], '?', true),
            ]);
        } catch (OssException $e) {
            return json(['code' => 0, 'msg' => $e->getMessage()]);
        }
    }

    // 直接上传完整文件
    public function directUpload()
    {
        try {
            $ossClient = new OssClient(
                config('alioss.accessKeyId'),
                config('alioss.accessKeySecret'),
                config('alioss.endpoint')
            );

            $file = $_FILES['file'];
            $file_name = $this->request->request('file_name', '');
            $object = 'uploads/' . date('Ymd') . '/' . $file['name'];

            $result = $ossClient->uploadFile(
                config('alioss.bucket'),
                $object,
                $file['tmp_name']
            );

            return json([
                'code' => 1,
                'url' => $result['oss-request-url'],
                'name' => pathinfo($file_name, PATHINFO_FILENAME),
                'fullurl' => $result['oss-request-url'],
            ]);
        } catch (OssException $e) {
            return json(['code' => 0, 'msg' => $e->getMessage()]);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2385224.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【场景分析】基于概率距离快速削减法的风光场景生成与削减方法

目录 1 主要内容 场景消减步骤 2 部分代码 3 程序结果 1 主要内容 该程序参考文献《含风光水的虚拟电厂与配电公司协调调度模型》场景消减部分模型&#xff0c;程序对风电场景进行生成并采用概率距离方法进行消减&#xff0c;程序先随机生成200个风电出力场景&#xff0c;然…

【Java Web】3.SpringBootWeb请求响应

&#x1f4d8;博客主页&#xff1a;程序员葵安 &#x1faf6;感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb; 文章目录 一、请求 1.1 postman 1.2 简单参数 1.3 实体参数 1.4 数组集合参数 1.5 日期参数 1.6 JSON参数 1.7 路径参数 二、响应 2…

单片机中断系统工作原理及定时器中断应用

文件目录 main.c #include <REGX52.H> #include "TIMER0.H" #include "KEY.H" #include "DELAY.H"//void Timer0_Init() { // TMOD 0x01; // TL0 64536 % 256; // TH0 64536 / 256; // ET0 1; // EA 1; // TR0 1; //}unsigned char…

LangGraph-agent-天气助手

用于创建agent和多代理工作流 循环&#xff08;有迭代次数&#xff09;、可控、持久 安装langgraph包 conda create --name agent python3.12 conda activate agent pip install -U langgraph pip install langchain-openai设置 windows&#xff08;>结尾&#xff09; s…

深度学习——超参数调优

第一部分&#xff1a;什么是超参数&#xff1f;为什么要调优&#xff1f; 一、参数 vs 超参数&#xff08;Parameter vs Hyperparameter&#xff09; 类型定义举例是否通过训练自动学习&#xff1f;参数&#xff08;Parameter&#xff09;是模型在训练过程中通过反向传播自动…

创建型:建造者模式

目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 工作流程 2.3 实现案例 2.4 变体&#xff1a;链式建造者&#xff08;常见于多参数对象&#xff0c;无需指挥者&#xff09; 3、优缺点分析 4、适用场景 1、核心思想 目的&#xff1a;将复杂对象的构建过程与其表示分离…

UE4游戏查找本地角色数据的方法-SDK

UE4中&#xff0c;玩家的表示通常涉及以下几个类&#xff1a; APlayerController: 代表玩家的控制逻辑&#xff0c;处理输入等。 APawn: 代表玩家在世界中的实体&#xff08;比如一个角色、一辆车&#xff09;。APlayerController 控制一个 APawn。 ACharacter: APawn 的一个…

Java 连接并操作 Redis 万字详解:从 Jedis 直连到 RedisTemplate 封装,5 种方式全解析

引言 在分布式系统和高并发场景中&#xff0c;Redis 作为高性能内存数据库的地位举足轻重。对于 Java 开发者而言&#xff0c;掌握 Redis 的连接与操作是进阶必备技能。然而&#xff0c;从基础的 Jedis 原生客户端到 Spring 封装的 RedisTemplate&#xff0c;不同连接方式的原…

python web 开发-Flask-Login使用详解

Flask-Login使用详解&#xff1a;轻松实现Flask用户认证 1. Flask-Login简介 Flask-Login是Flask框架的一个扩展&#xff0c;专门用于处理用户认证相关的功能。它提供了用户会话管理、登录/注销视图、记住我功能等常见认证需求&#xff0c;让开发者能够快速实现安全的用户认证…

快速排序算法的C++和C语言对比

快速排序算法简介&#xff1a; 快速排序(Quick Sort)是一种高效的排序算法&#xff0c;采用分治法策略。它的基本思想是&#xff1a; 1. 从数列中挑出一个元素作为"基准" 2. 重新排序数列&#xff0c;所有比基准值小的元素放在基准前面&#xff0c;所有比基准值大的…

分布式事务知识点整理

目录 分布式事务问题&#xff1f;问题场景引入分布式事务的理论标准BASE理论附CAP理论 Two-phase Commit&#xff0c;2PC2PC系统组件两阶段执行过程2PC缺点 Three-Phase Commit&#xff0c;3PC三阶段执行过程 TTC(Try-Confirm-Cancel)seata项目以及原理how to define a Distrib…

鸿蒙UI开发——badge角标的使用

1、概 述 badge小红点角标是我们项目开发中常见的需求&#xff0c;信息标记组件&#xff0c;可以附加在单个组件上用于信息提醒的容器组件。效果如下&#xff1a; 2、Badge 接口定义如下&#xff1a; &#x1f449;&#x1f3fb; 根据数字创建标记组件&#xff1b; Badge(v…

批量打印的趣事

前言 PC端网页打印大量数据的时候&#xff0c;比如批量打印100个标签&#xff0c;会出现打印样式混乱的问题 问题 数据可以设定100~自定义阈值 {data.map((_, idx) > {return <Tag qrCode啊程是个大帅逼 code{AB-${idx1}} title雷猴 key{idx} />})} 打印预览到第3…

车载中央域控制器测试【BCM模块介绍-外灯3】

文章目录 1 摘要2 倒车灯2.1 倒车灯的作用与功能2.2 控制实现方案2.3 需求分析2.3.1系统需求2.3.2 功能安全需求&#xff08;ISO 26262 ASIL B&#xff09;*2.3.3 关联功能需求 3 角灯3.1 角灯&#xff08;Cornering Lamp&#xff09;核心作用与功能3.2 控制实现方案3.3 需求分…

Linux系统基础——是什么、适用在哪里、如何选

一、Linux是什么 Linux最初是由林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;基于个人兴趣爱好开发的个人项目&#xff0c;他编写了最核心的内核&#xff1b;后面为了发展壮大Linux系统他将整个项目开源到GitHub上&#xff0c;可以让全世界的人都参与到项目的开发维护中…

C++标准库中 std::string 类提供的 insert 成员函数的不同重载版本

下图是C标准库中 std::string 类提供的 insert 成员函数的不同重载版本&#xff0c;可点击C标准库获取 以下是std::string::insert各重载版本的功能及参数解释&#xff1a; 1. 插入完整字符串 string& insert(size_t pos, const string& str); 功能&#xff1a;在字…

Qt window frame + windowTitle + windowIcon属性(3)

文章目录 window frame属性window frame的概念1. window frame的影响2. 图片演示3. 代码演示 API接口widget.cpp&#xff08;测试代码&#xff09; windowTitle属性API接口问题 注意点widget.cpp&#xff08;属性用法&#xff09; windowIcon属性API接口啥是窗口图标玩法1. 先…

Dify源码学习

文章目录 1 大模型基本原理1.1 model_context_tokens、max_tokens和prompt_tokens1.1.1 三者之间的关系1.1.2 总结对比 2 Dify源代码2.0 前后端代码跑起来【0】准备开发环境【1】下载代码【2】运行后端&#xff08;1&#xff09;Start the docker-compose stack&#xff08;2&a…

静态网站部署:如何通过GitHub免费部署一个静态网站

GitHub提供的免费静态网站托管服务可以无需担心昂贵的服务器费用和复杂的设置步骤&#xff0c;本篇文章中将一步步解如何通过GitHub免费部署一个静态网站&#xff0c;帮助大家将创意和作品快速展现给世界。 目录 了解基础情况 创建基础站点 在线调试站点 前端项目部署 部署…

【拯救小狗】2022-1-3

缘由c学校练习试题&#xff0c;求解决-编程语言-CSDN问答 void 拯救小狗() {//缘由https://ask.csdn.net/questions/7622294?spm1005.2025.3001.5141int d 0, g 0, tfh[100][3]{}, x 0, c 10, dd d;std::cin >> d >> g; dd d;while (x < g && d…