图片上传咋这么慢

“取一杯天上的水,照着明月人世间晃呀晃” 手机电话来了,一个陌生的本地电话号码,我接了电话。

“小飞吗?”

“是的”

“我是xx项目的曹总,管理端的商品图片上传有点慢,可以优化吗?”

“曹总,好的,我这边帮您处理下。” 挂了电话后,我赶紧拿出了我的电脑。

这个项目是公司新接的一个电商项目,图片上传的功能是一般是用的比较多的。

“存储图片用的是阿里云,上传速度应该不会这么慢,是不是曹总那边带宽问题?” 我边想边打开 Google 浏览器,输入管理端登录地址,登录管理员账号。

打开商品管理,新增商品,上传一张小图片,秒传,上传一张几M的就有点慢。那应该不是带宽问题。我这边带宽是百兆带宽。有人会问,“你不是流量限速了吗?怎么会有百兆带宽",因为我现在在公司加班(蹭流量)。

排除了是本地带宽问题,按键盘 F12 ,打开浏览器调试模式,点击上传,发现只调用了一个接口,还是请求后台的。

image-20210502065701528

打开 IDEA,ctrl+shift+f 搜索图片上传的代码

image-20210502070120469

一般像阿里云、七牛云上传,都是直传,直传就是不经过我们应用服务器,直接上传到阿里云或七牛云存储服务器。

问题就很明显了,因为这个项目的服务器带宽是有限制的,图片上传是先上传到我们应用服务器,然后应用服务器再上传到阿里云服务器。

image.png

直传

下图是阿里云存储直传的流程图

时序图

当用户要上传一个文件到OSS,而且希望将上传的结果返回给应用服务器时,需要设置一个回调函数,将请求告知应用服务器。用户上传完文件后,不会直接得到返回结果,而是先通知应用服务器,再把结果转达给用户。

注意一下,阿里云存储和七牛云存储还有点区别,如果你不设置回调,阿里云就不会返回你图片URL。

上传密钥申请

  • endpoint

image-20210502071426667

  • accessKey、secretKey

image-20210502071509346

image-20210502071723673

image-20210502071834182

创建用户,需要手机号验证

image-20210502072355526

添加权限

  • bucket

image-20210502072841185

  • fileUrl (bucket+enpoint)

比如 bucket = demo ,enpoint = oss-cn-hangzhou.aliyuncs.com

那 fileUrl = demo.oss-cn-hangzhou.aliyuncs.com

后端代码

集成阿里云上传SDK

<dependency>
     <groupId>com.aliyun.oss</groupId>
     <artifactId>aliyun-sdk-oss</artifactId>
     <version>3.8.0</version>
</dependency>

初始化 ossClient

private OSS getOssClient() {
    return new OSSClientBuilder().build(endpoint, accessKey, secretKey);
}

获取上传token

/**
* 获取上传token 
* @param dir 文件指定目录 比如 product/demo.jpg 就传 product
*/
public Map getToken(String dir) {
    OSS client = getOssClient();
    // 过期时间
    long expireTime = 30;
    long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
    Date expiration = new Date(expireEndTime);
    PolicyConditions policyConditions = new PolicyConditions();
    policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
    policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

    String postPolicy = client.generatePostPolicy(expiration, policyConditions);

    byte[] binaryData = new byte[0];
    try {
    	binaryData = postPolicy.getBytes("utf-8");
    } catch (UnsupportedEncodingException e) {
    	throw new UploadException("阿里云上传失败:", e);
    }
    String encodedPolicy = BinaryUtil.toBase64String(binaryData);
    String postSignature = client.calculatePostSignature(postPolicy);

    Map<String, String> respMap = new LinkedHashMap<>();
    respMap.put("OSSAccessKeyId", param.accessKey);
    respMap.put("policy", encodedPolicy);
    respMap.put("signature", postSignature);
    respMap.put("dir", dir);
    respMap.put("host", param.fileUrl);
    respMap.put("expire", String.valueOf(expireEndTime / 1000));
    return respMap;
}

控制层代码

@RequestMapping(value = "/getToken")
@ResponseBody
public AjaxResult getUploadToken(String dir,String suffix) {
    Map map = (Map)FileUploadUtil.getToken(UploadPlatform.Ali, dir);
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("callbackUrl","http://demo.com/callback");
    jsonObject.put("callbackBody","filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
    jsonObject.put("callbackBodyType","application/x-www-form-urlencoded");
    String s = jsonObject.toString();
    String encode = Base64.encode(s);
    map.put("callback",encode);
    map.put("key",map.get("dir")+"/"+RandomUtil.randomString(16)+"."+suffix);
    return AjaxResult.success(map);
}

/**
* 回调
*/
@RequestMapping(value = "/callback")
@ResponseBody
public Map callback(HttpServletRequest request) {
    String fileName = request.getParameter("filename");
    String size = request.getParameter("size");
    String mimeType = request.getParameter("mimeType");
    String height = request.getParameter("height");
    String width = request.getParameter("width");
    Map map = new HashMap();
    map.put("url",fileName);
    return map;
}

前端代码

upload.js

import request from '@/utils/request'

// 获取上传token
export function getToken(query) {
  return request({
    url: '/upload/getToken',
    method: 'get',
    params: query
  })
}

图片上传组件

<template>
  <div>
    <el-upload
      :action="host"
      :before-upload="beforeUpload"
      :data="reqData"
      :file-list="fileList"
      :limit="1"
      :on-error="uploadError"
      :on-exceed="handleExceed"
      :on-remove="handleRemove"
      :on-success="uploadSuccess"
      list-type="text"
    >
      <el-button size="small" slot="trigger" type="primary">点击上传</el-button>
      <div class="el-upload__tip" slot="tip">只能上传 apk 文件,且不超过10M</div>
    </el-upload>
  </div>
</template>

<script>
  import {getToken} from "@/api/system/upload.js";
  import md5 from 'blueimp-md5'

  export default {
    props: {
      value: {type: String},
      dir: {
        type: String,
        default: "default"
      },
      fileName: {type: String}
    },
    data() {
      return {
        fileList: [],
        host: 'http://xxxx.oss-cn-beijing.aliyuncs.com/',
        reqData: {},
        fileUrl: "",
      }
    },
    created() {
      if (this.value) {
        this.fileUrl = this.host + this.value;
        this.fileList = [{name: this.fileName, url: this.fileUrl}]
        console.log(this.fileList)
      }
    },
    methods: {
      // 获取阿里云数据
      async getAliyunToken(query) {
        const {data} = await getToken(query);
        this.reqData.OSSAccessKeyId = data.OSSAccessKeyId;
        this.reqData.policy = data.policy;
        this.reqData.signature = data.signature;
        this.reqData.success_action_status = 200;
        this.reqData.callback = data.callback;
      },
      emitInput(url, name) {
        this.$emit("uploadFile", url, name);
      },
      handleRemove(file, fileList) {
        this.emitInput('', '');
      },
      uploadSuccess(response, file, fileList) {
        if (response.url) {
          this.fileUrl = this.host + response.url;
        }
        this.$emit("uploadFile", this.reqData.key);
      },
      handleExceed(files, fileList) {
        this.$message.warning(
          `当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`
        );
      },
      uploadError(err, file, fileList) {
        this.$message({
          message: "上传出错,请重试!",
          type: "error",
          center: true
        });
      },
      async beforeUpload(file) {
        console.log(file);
        // todo:上传文件的类型控制....
        // const isVideo = file.type === 'video/mp4' || file.type === 'video/ogg' || file.type === 'video/flv' || file.type === 'video/avi' || file.type === 'video/wmv' || file.type === 'video/rmvb';
        // const isLt10M = file.size / 1024 / 1024 < 10;
        // if (!isVideo) {
        //   this.$message.warning('请上传正确格式的视频!');
        //   return false;
        // } else {
        //   if (!isLt10M) {
        //     this.$message.warning('上传视频文件大小不能超过 10MB!');
        //     return false;
        //   }
        // }
         var index1 = file.name.lastIndexOf(".");
         var index2 = file.name.length;
         var suffix = file.name.substring(index1 + 1, index2);
        
        let curr = (+new Date()).toString()
        let random = Math.random() * 10000
        let md5Str = md5(`${curr}${random}${file.name}`)
        this.reqData.key = this.dir + '/' + md5Str + "." + suffix;

        let query = {dir: this.dir};
        await this.getAliyunToken(query)
        console.log(this.reqData.key);
      },

    }
  }
</script>

我这边是用 el-upload 集成的 ,后端人员也可以直接用 postman 进行测试

image-20210502075457301

参考

服务端签名直传并设置上传回调

oss上传官方文档

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值