likes
comments
collection
share

图片传输速度优化 阿里云OSS

作者站长头像
站长
· 阅读数 21

一、背景

最近接到一个需求,公司的一个演示站点上的图片加载非常慢,首次加载需要10s往上,影响用户使用体验,需要优化一下。

首先明确的是站点是部署在华东地区的阿里云服务器上,且后续会有非华东地区的用户使用,如华北或者国外用户。

其次由于这个站点只是演示性质,前期做的较为粗糙,图片直接在多端传输,影响性能。

二、方案

1、阿里云OSS+传输加速

在看到上述背景之后,首先想到的是既然站点是部署在阿里云服务器上,那是否可以借助阿里云的OSS服务来提高图片的上传下载速度。

查阅相关资料之后,常用的方案是OSS+CDN,但是阿里云OSS还直接提供传输加速服务,于是可选的方案就变成了CDN和传输加速的比较。

我们来看看两者有什么区别

  • CDN加速OSS:是建立并覆盖在承载网之上,由遍布全球的边缘节点服务器群组成的分布式网络。阿里云CDN能分担源站压力,避免网络拥塞,确保在不同区域、不同场景下加速网站内容的分发,提高资源访问速度。由CDN全球广泛分布的边缘节点缓存OSS存储的静态数据,从而实现客户端从边缘节点直接获取数据的方式来实现访问的加速。
  • OSS传输加速:利用全球分布的云机房,将全球各地用户对您存储空间(Bucket)的访问,经过智能路由解析至就近的接入点,使用优化后的网络及协议,为云存储互联网的上传、下载提供端到端的加速方案。

说白了就是阿里云CDN是利用边缘节点缓存进行加速,而传输加速则是对传输链路以及协议策略进行优化,两者还是有本质上的区别的。

最后综合考虑了一下时间以及改造的难易程度,最后还是采用了阿里云的传输加速(偷懒.jpg)

即最终方案是阿里云OSS+传输加速(需额外付费)。

2、服务端签名直传

由于站点上有上传图片的需求,之前是直接传输图片到应用服务器,网络消耗大,速度慢。

和数据直传到OSS相比,存在以下缺点:

  • 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS,网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • 扩展性差:如果后续用户数量逐渐增加,则应用服务器会成为瓶颈。
  • 费用高:需要准备多台应用服务器。由于OSS上行流量是免费的,如果数据直传到OSS,将节省多台应用服务器的费用。

现在采用了阿里云OSS+传输加速,需将Web端改造为直传阿里云OSS,提高上传速度,端与端之间的图片皆通过url传输。

Web端直传OSS又有以下三种方案:

  • 利用OSS Browser.js SDK将文件上传到OSS
  • 使用表单上传方式将文件上传到OSS
  • 通过小程序上传文件到OSS

我们采用表单上传方式中的服务端签名直传

服务端签名直传是指在服务端生成签名,将签名返回给客户端,然后客户端使用签名上传文件到 OSS 。由于服务端签名直传无需将访问密钥暴露在前端页面,相比客户端签名直传具有更高的安全性

图片传输速度优化 阿里云OSS

三、实践

1、新建bucket,并开启传输加速

1.1 创建子用户

图片传输速度优化 阿里云OSS

图片传输速度优化 阿里云OSS

注意保存生成的accesskey

图片传输速度优化 阿里云OSS

1.2 新建bucket

新建bucket,读写权限设置为公共读

图片传输速度优化 阿里云OSS

1.3 跨域策略

图片传输速度优化 阿里云OSS

1.4 生命周期设置(可忽略)

上传的的临时文件删除

图片传输速度优化 阿里云OSS

1.5 开启传输加速

图片传输速度优化 阿里云OSS

注意:开启之后会有传输加速域名,通过此域名才进行传输加速,并会单独付费

2、后端改造

2.1 Gradle配置SDK

implementation 'com.aliyun.oss:aliyun-sdk-oss:3.17.1'

2.2 OSS初始化

@Data
@Component
@ConfigurationProperties(prefix = "xxx.xxx")
public class AliyunOSSProperties {

    private String endpoint;

    private String accessKey;

    private String secretKey;

    private Long expire;

}
@Data
@Configuration
public class AliyunOSSConfiguration {

    @Autowired
    private AliyunOSSProperties aliyunOSSProperties;

    @Bean
    public OSS ossClient() {
        return new OSSClientBuilder().build(
                aliyunOSSProperties.getEndpoint(),
                aliyunOSSProperties.getAccessKey(),
                aliyunOSSProperties.getSecretKey());
    }

}

2.3 接口

//开启OSS客户端
long expireTime = aliyunOSSProperties.getExpire();
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;

PolicyConditions policyConditions = new PolicyConditions();
policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

//根据到期时间生成policy
Date expiration = new Date(expireEndTime);
String postPolicy = oss.generatePostPolicy(expiration, policyConditions);

//对policy进行UTF-8编码后转base64
byte[] binaryData = new byte[0];
try {
    binaryData = postPolicy.getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
    throw new BusinessException("阿里云上传失败:", e);
}

String encodedPolicy = BinaryUtil.toBase64String(binaryData);

//生成signature
String postSignature = oss.calculatePostSignature(postPolicy);

AliyunOSSPolicyCO aliyunOSSPolicyCO = new AliyunOSSPolicyCO();
aliyunOSSPolicyCO.setAccessKey(aliyunOSSProperties.getAccessKey());
aliyunOSSPolicyCO.setPolicy(encodedPolicy);
aliyunOSSPolicyCO.setSignature(postSignature);
aliyunOSSPolicyCO.setExpire(expireEndTime);

return aliyunOSSPolicyCO;

3、前端改造

前端采用的是Ant Design的Upload组件

import React, {useEffect, useState} from 'react';
import {UploadOutlined} from '@ant-design/icons';
import type {UploadProps} from 'antd';
import {Button, message, Upload} from 'antd';
import type {UploadFile} from 'antd/es/upload/interface';
import {generateSign} from '@/services/xx/AliyunOSS';
import styles from "./index.less";

interface AliyunOSSUploadProps {
  value?: UploadFile[];
  onChange?: (fileList: UploadFile[]) => void;
  setOrginalPicUrl: React.Dispatch<React.SetStateAction<any[]>>;
}

const AliyunOSSUpload : React.FC<AliyunOSSUploadProps> = (props) => {

  const { value, onChange, setOrginalPicUrl } = props;

  const [OSSData, setOSSData] = useState<API.AliyunOSSPolicyCO>();

  const init = async () => {
    try {
      const result = await generateSign({dir: "upload"});

      if (!result || !result.success) return;
      console.log("签名请求结果: ", result.data);

      setOSSData(result.data);
    } catch (error) {
      message.error(error);
    }
  };

  useEffect(() => {
    init();
  }, []);

  const handleChange: UploadProps['onChange'] = ({ fileList , file}) => {
    onChange?.([...fileList]);

    if (file.status == 'done') {
      var url = "http://xxx" + file.originFileObj.url;
      setOrginalPicUrl(url);
    }

  };

  const onRemove = (file: UploadFile) => {
    const files = (value || []).filter((v) => v.url !== file.url);

    if (onChange) {
      onChange(files);
    }
  };

  const getExtraData: UploadProps['data'] = (file) => ({
    key: file.url,
    OSSAccessKeyId: OSSData?.accessKey,
    policy: OSSData?.policy,
    Signature: OSSData?.signature,
  });

  const beforeUpload: UploadProps['beforeUpload'] = async (file) => {
    if (!OSSData) return false;

    const expire = OSSData.expire;

    if (expire < Date.now()) {
      await init();
    }

    const suffix = file.name.slice(file.name.lastIndexOf('.'));
    const filename = Date.now() + suffix;
    // @ts-ignore
    file.url = 'upload/' + filename;

    return file;
  };

  const uploadProps: UploadProps = {
    name: 'file',
    fileList: value,
    action: "http://xxx",
    onChange: handleChange,
    onRemove,
    data: getExtraData,
    beforeUpload,
  };

  return (
    <Upload {...uploadProps} showUploadList={false} className={styles.container}>
      <Button type="primary" icon={<UploadOutlined />}>Upload</Button>
    </Upload>
  );
};

export default AliyunOSSUpload;

四、后续改进思路

1、采用阿里云OSS+CDN

2、读写权限改为私有

五、参考文档

  1. CDN加速和传输加速的区别:help.aliyun.com/zh/oss/diff…
  2. 服务端签名直传:help.aliyun.com/zh/oss/use-…
  3. Ant design upload组件:ant.design/components/…
转载自:https://juejin.cn/post/7268080903095156771
评论
请登录