上传文件很费时费力?那是你没用对方式

news2024/11/18 13:42:06

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、前端直传的优点
  • 二、实现步骤
    • 2.1、后端方面
      • 2.1.1 添加依赖
      • 2.1.2 增加接口
      • 2.1.3 测试接口
    • 2.2、前端方面
      • 2.2.1 安装 cos-js-sdk-v5 依赖
      • 2.2.2 新建组件
      • 2.2.3 使用组件
  • 总结


前言

嗨,大家好,我是希留,一个被迫致力于全栈开发的老菜鸟。

上一篇文章说到上传文件使用云服务商的对象存储,感兴趣的可以阅读该文章:传送门

发布后有不少伙伴反馈,前后端分离的项目更好的上传方式是使用前端直传的方式。于是我查阅相关文档,连夜把项目里的上传方式改成前端直传了(项目的技术栈是Springboot + Vue),发现上传速度明显提升了。所以这篇文章就来说说,前端直传的方式应该怎么弄呢?


一、前端直传的优点

前端数据直传的方式相比较后端上传的方式有不少的优点。

  • 上传速度快。后端上传的方式是用户数据需先上传到应用服务器,之后再上传到COS。而前端直传的方式,用户数据不用通过应用服务器中转,直传到COS。减少了网络请求响应,速度将大大提升。而且COS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • 扩展性好。后端上传的方式会占用带宽,文件上传一多就会占用服务器大量的带宽,导致其它请求阻塞,甚至无法访问等情况。前端直传的方式节约了后端服务器的带宽和负载。
  • 成本低。服务器真正的成本基本上都在带宽上,提升带宽的费用是很贵的。而使用前端直传的方式可以减少文件上传到服务器的带宽,从而降低带宽成本。

二、实现步骤

2.1、后端方面

后端需要提供一个生成临时密钥的接口,具体步骤如下,可参考文档,由于我的后端服务是Java语言,所以这里举例使用的是 Java SDK。

2.1.1 添加依赖

<!--腾讯云 COS 临时密钥-->
<dependency>
    <groupId>com.qcloud</groupId>
    <artifactId>cos-sts_api</artifactId>
    <version>3.1.1</version>
</dependency>

2.1.2 增加接口

代码如下(示例):

package com.xiliu.common.controller;

import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Response;
import com.xiliu.common.result.R;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.TreeMap;

/**
 * @author xiliu
 * @description cos存储前端控制器
 * @date 2022/11/13 17:51
 */
@RestController
@RequestMapping("/cos")
public class CosController {

    @Value("${cos.secretId}")
    private String secretId;
    @Value("${cos.secretKey}")
    private String secretKey;
    @Value("${cos.regionName}")
    private String regionName;
    @Value("${cos.bucketName}")
    private String bucketName;

    @ApiOperation(value = "获取cos临时密钥")
    @GetMapping("/temp-key")
    public R getTempKey() {
        TreeMap<String, Object> config = new TreeMap<String, Object>();
        try {
            // 替换为您的云 api 密钥 SecretId
            config.put("secretId", secretId);
            // 替换为您的云 api 密钥 SecretKey
            config.put("secretKey", secretKey);
            // 临时密钥有效时长,单位是秒,默认 1800 秒,目前主账号最长 2 小时(即 7200 秒),子账号最长 36 小时(即 129600)秒
            config.put("durationSeconds", 1800);
            // 换成您的 bucket
            config.put("bucket", bucketName);
            // 换成 bucket 所在地区
            config.put("region", regionName);

            // 只允许用户访问 upload/house 目录下的资源
            config.put("allowPrefixes", new String[] {"upload/house/*"});

            // 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
            String[] allowActions = new String[] {
                    // 简单上传
                    "name/cos:PutObject",
                    // 表单上传、小程序上传
                    "name/cos:PostObject",
                    // 分块上传
                    "name/cos:InitiateMultipartUpload",
                    "name/cos:ListMultipartUploads",
                    "name/cos:ListParts",
                    "name/cos:UploadPart",
                    "name/cos:CompleteMultipartUpload"
            };
            config.put("allowActions", allowActions);

            Response response = CosStsClient.getCredential(config);
            return R.ok(response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("no valid secret !");
        }
    }
}

2.1.3 测试接口

由于是测试,接口的安全校验我就去掉了,方便测试是否能正常生成临时密钥。结果如下图所示,正常生成了。在这里插入图片描述

2.2、前端方面

前端方面,可以参考文档,我使用的前端框架是Vue,具体步骤如下,上传功能可以封装成一个组件,提供给有需要的地方调用。

2.2.1 安装 cos-js-sdk-v5 依赖

npm install --save cos-js-sdk-v5

2.2.2 新建组件

在 components 目录下新建一个 upload 目录,在 upload 目录下新建 multiUpload.vue 组件。

代码示例如下:

<template> 
  <div>
    <el-upload
      action=""
      list-type="picture-card"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :http-request="handleUploadFile"
      :on-preview="handlePreview"
      :limit="maxCount"
      :on-exceed="handleExceed"
    >
      <i class="el-icon-plus"></i>
    </el-upload>
    <!--进度条-->
    <el-progress v-show="showProgress"  :text-inside="true" :stroke-width="15"
       :percentage="progress" status="success"></el-progress>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="dialogImageUrl" alt="">
    </el-dialog>
  </div>
</template>
<script>
  import COS from 'cos-js-sdk-v5'
  import { getCosTempKey } from '@/api/upload'

  export default {
    name: 'multiUpload',
    props: {
      //图片属性数组
      value: Array,
      //最大上传图片数量
      maxCount:{
        type:Number,
        default:9
      }
    },
    data() {
      return {
        // 图片预览
        dialogVisible: false,
        // 图片预览地址
        dialogImageUrl:null,
        // COS
        cosData: {},
        // 进度条的显示
        showProgress: false,
        // 进度条数据
        progress: 0,
        // 文件表单
        fileParams: {
          // 上传的文件目录
          folder: '/upload/house/'
        },
      };
    },
    computed: {
      fileList() {
        let fileList=[];
        for(let i=0;i<this.value.length;i++){
          fileList.push({url:this.value[i]});
        }
        return fileList;
      }
    },
    methods: {
      emitInput(fileList) {
        let value=[];
        for(let i=0;i<fileList.length;i++){
          value.push(fileList[i].url);
        }
        this.$emit('input', value)
      },
      // 移除图片
      handleRemove(file, fileList) {
        this.emitInput(fileList);
      },
      // 预览图片
      handlePreview(file) {
        this.dialogVisible = true;
        this.dialogImageUrl = file.url;
      },
      // 上传预处理
      beforeUpload(file) {
        return new Promise((resolve, reject) => {
          const isImage = file.type.indexOf("image/") != -1;
          const isLt2M = file.size / 1024 / 1024 < 10;

          if (!isImage) {
            this.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
            reject(false)
          }
          if (!isLt2M) {
            this.$message.error("上传头像图片大小不能超过 10MB!");
            reject(false)
          }
          // 获取cos临时密钥
          getCosTempKey().then(response => {
            this.cosData = response.data;
            resolve(true)
          }).catch(error => {
            this.$message.error('获取cos临时密钥失败!msg:' + error)
            reject(false)
          })
        })
      },
      // 前端直传文件
      handleUploadFile(file) {
        let that = this;
        // 获取COS实例
        const cos = new COS({
          // 必选参数
          getAuthorization: (options, callback) => {
            const obj = {
              TmpSecretId: that.cosData.credentials.tmpSecretId,
              TmpSecretKey: that.cosData.credentials.tmpSecretKey,
              XCosSecurityToken: that.cosData.credentials.sessionToken,
              // 时间戳,单位秒,如:1580000000
              StartTime: that.cosData.startTime,
              // 时间戳,单位秒,如:1580000900
              ExpiredTime: that.cosData.expiredTime
            }
            callback(obj)
          }
        });
        // 文件路径和文件名
        let cloudFilePath = this.fileParams.folder + `${+new Date()}` + '_' + file.file.name

        // 执行上传服务
        cos.putObject({
          // 你的存储桶名称
          Bucket: 'xiliu-1259663924',
          // 你的存储桶地址
          Region: 'ap-guangzhou',
          // key加上路径写法可以生成文件夹
          Key: cloudFilePath,
          StorageClass: 'STANDARD',
          // 上传文件对象
          Body: file.file,
          onProgress: progressData => {
            if (progressData) {
              that.showProgress = true
              that.progress = Math.floor(progressData.percent * 100)
            }
          }
        },(err, data) => {
          if (data && data.statusCode === 200) {
            let uploadResult = `https://${data.Location}`
            that.showProgress = false
            that.$message({message: '上传成功', type: 'success'})
            this.fileList.push({name: file.name,url:uploadResult});
            this.emitInput(this.fileList);
          } else {
            that.$message.error("上传失败,请稍后重试!")
          }
        })
      },
      // 文件超出个数限制
      handleExceed(files, fileList) {
        this.$message({
          message: '最多只能上传'+this.maxCount+'张图片',
          type: 'warning',
          duration:1000
        });
      },
    }
  }
</script>
<style>

</style>

2.2.3 使用组件

代码示例如下:

<el-form-item label="房屋图片/视频:">
  <multi-upload v-model="selectHousePics"></multi-upload>
</el-form-item>

效果如下图所示:
在这里插入图片描述

总结

以上就是本文的全部内容了,感谢大家的阅读。

如果觉得文章对你有帮助,还不忘帮忙点赞、收藏、关注、评论哟,您的支持就是我创作最大的动力!


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

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

相关文章

你的哪些SQL慢?看看MySQL慢查询日志吧

前言 在项目里面&#xff0c;多多少少都隐藏着一些执行比较慢的SQL, 不同的开发测试人员在平时使用的过程中多多少少都能够遇到&#xff0c;但是无法立马有时间去排查解决。那么如果有一个文件能够将这些使用过程中比较慢的SQL记录下来&#xff0c;定期去分析排查&#xff0c;…

meta视口标签

属性解释说明width宽度设置的是viewport宽度&#xff0c;可以设置device-width特殊值initial-scale初始缩放比&#xff0c;大于0的数字maximum-scale最大缩放比&#xff0c;大于0的数字minimum-scale最小缩放比&#xff0c;大于0的数字user-scalable用户是否可以缩放&#xff0…

关于我们编写好的java程序是如何运行部署的

了解如何去查看项目如何运行怎么部署java项目部署到服务器的程序和在本地运行的程序有什么不同java中的Class文件是如何形成的Class文件如何执行的怎么部署java项目 首先宏观的说一下&#xff0c;程序的运行都是要有一个启动入口的&#xff0c;也就是我们经常说的main函数是程…

【Shell 脚本速成】03、Shell 脚本实战案例(一)数据磁盘初始化

目录 一、案例应用场景 二、案例需求 脚本所需相关知识点 三、案例算法 四、代码实现 五、实现验证 一、案例应用场景 生产环境中的服务器一般会分为系统盘和数据盘两种磁盘&#xff0c;以dell R730举例&#xff0c;该服务器是一个2U的机架式服务器&#xff0c;满载可以挂…

HTML+CSS简单的网页制作期末作业——浙江旅游景点介绍网页制作

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

Redisson分布锁原理分析及源码解读

本文源码解读基于Redisson 3.18.0 版本 Redisson分布锁实现原理 Redisson锁实现基本原理大致如下图所示&#xff1a; 客户端执行Lua脚本去获取锁&#xff0c;如果获取失败&#xff0c;则订阅解锁消息&#xff0c;并挂起线程。 客户端解锁时执行一段Lua脚本&#xff0c;删除锁的…

[附源码]计算机毕业设计JAVA课程资源系统管理

[附源码]计算机毕业设计JAVA课程资源系统管理 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

PowerJob 定时从SFTP下载文件拆的坑

一. 业务需求 SFTP上有多个目录, 每小时要下载一次文件, 每个目录的下载任务都是一个独立的工作流任务. 二.问题描述 手动执行每个任务可以正常执行, 但是当所有任务都开启定定时任务执行时(每小时执行一次),任务实例就会报错. 三.问题分析 查看服务端和worker端的日志, …

数据采集-“消防知识网上答题挑战赛”题库

为普及消防法律法规和消防安全知识&#xff0c;提升全员消防安全意识&#xff0c;提高抗御火灾、自防自救和组织疏散能力&#xff0c;集团公司近日部署11月份集中开展消防宣传月活动。 为“全民消防”营造浓厚氛围&#xff0c;集团公司以消防知识为主要内容&#xff0c;整理形…

关于如何快速学好,学懂Linux内核。内含学习路线

学习linux内核&#xff0c;这个可不像学一门语言&#xff0c;c或者java一个月或者3月你就能精通掌握。学习linux内核是需要一步一步循序渐进&#xff0c;掌握正确的linux内核学习路线对学习至关重要&#xff0c;本篇文章就来分享学习linux内核的一些建议吧。 1. 了解操作系统基…

nginx(六十一)proxy模块(二)修改发往上游的请求

一 修改发往上游的请求 重点&#xff1a; 利用指令更改转发给上游服务器的HTTP报文的内容1) 请求行 --> proxy_method、url、proxy_http_version2&#xff09;请求头 --> proxy_set_header、proxy_pass_request_headers3&#xff09;请求体 --> proxy_pass_request_b…

SpringBoot入口深入

这里写目录标题1.run&#xff08;&#xff09;1.1 程序运行监听器 SpringApplicationRunListeners1.2 应用参数 ApplicationArguments启动加载顺序1.run&#xff08;&#xff09; run()方法是一个SpringBoot程序的入口 SpringApplication.run(Application.class, args);看看方…

OVIS数据集代码解析

OVIS数据集代码解析 OVIS数据集格式整体和COCO类似&#xff0c;但是是以video的形式存储的&#xff0c;对应的解析代码见&#xff1a;https://github.com/qjy981010/cocoapi/blob/main/PythonAPI/pycocotools/ovis.py。 由于OVIS仅train提供了标注&#xff0c;因此&#xff0…

前端埋点实现方案

前言 领导今天又来活了&#x1f623;&#xff0c;要记录每个页面的停留时间&#xff0c;以及页面的操作&#xff0c;是由哪个页面跳转过来的&#xff0c;给每个页面生成GUID上报给服务端&#xff0c;并且需要携带设备型号和设备唯一标识&#x1f644; 名称解释 UV&#xff0…

蓝牙运动耳机排行榜,目前排名最好的运动耳机推荐

对于运动人士来说&#xff0c;运动过程中佩戴着耳机听歌&#xff0c;既能让运动变得更加激情有动力&#xff0c;同时还能释放压力&#xff0c;放松心情。在选择运动耳机方面的要求也高&#xff0c;运动耳机不仅需要佩戴稳固舒适&#xff0c;它的防水性能也不能差&#xff0c;当…

Java并发常见面试题(一)

进程和线程 进程 进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的过程。 在Java中&#xff0c;当我们启动main函数其实就是启动了一个JVM进程&#xff0c;而main函数所在的线…

封装自己的 jquery 框架

说到jquery&#xff0c;那就给大家先简单的介绍一下什么是jquery jquery是JavaScript函数的一种封装。jQuery封装了JavaScript常用的功能代码&#xff08;函数&#xff09;&#xff0c;提供一种简便的JavaScript设计模式&#xff0c;优化HTML文档操作、事件处理、动画设计和Aja…

机器人运动学【2】

目录1.刚体状态的表达2.顺向运动学及DH表3.逆向运动学1.刚体状态的表达 我们前面已经学习了刚体移动和转动的表达&#xff0c;那么怎么将两者在数学上结合呢&#xff1f;这里我们开始构造如下矩阵&#xff0c;记作: 下面我们来看一下只有移动情况下的刚体的描述&#xff1a; …

使用canal解决Mysql和Redis数据同步(TCP)

使用canal解决Mysql和Redis数据同步(TCP) 工作原理分析 我们在面试的时候常常听面试官问这么一个问题&#xff1a;你们的Mysql和Redis怎么做数据同步的&#xff0c;根据不同的业务场景又很多方案&#xff0c;你可能会说先写库再删缓存&#xff0c;或者延迟双删或其他方案。今…

springboot集成flowable简单实例入门

此案例是demo。功能有创建流程、完成审批、生成流程图。适合有java基础的人员看。 第一步.画流程图 resources资源包下&#xff0c;新建processes包&#xff0c;新建一个文件&#xff0c;我命名他apply-rest.bpmmn20.xml。bpmn20.xml后缀文件是流程图配置文件。idea的右下角的…