antd Upload组件上传文件到阿里云OSS

前言

阿里云oss上传文件包含有好多SDK,一般像客户端的百度云盘这样的C端会使用如java相关的SDK,而浏览器端,也就是web端最好使用browser.js相关的SDK,下面就来讲解下如何使用,希望能够帮助到大家。

配置ram权限获取sts token和跨域支持

在使用阿里云OSS上传文件之前,要提前配置好ram相关权限,官方文档有说明,详细请看使用STS临时访问凭证访问OSS,配置时,要注意两点:
1、因为会使用上传和下载两种权限,所以应该使用下面权限策略(xxx为你的路径):

{
    "Version": "1",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "oss:PutObject",
                "oss:GetObject"
            ],
            "Resource": [
                "acs:oss:*:*:xxx",
                "acs:oss:*:*:xxx/*"
            ]
        }
    ]
}

配置好之后,我们再来配置后台,返回sts token给前端

 @Override
    public AssumeRoleResponse getAcsForUploadFile(String userName) {
        // STS接入地址,例如sts.cn-hangzhou.aliyuncs.com。
        String endpoint = aliyunOssConfig.getRamStsEndpoint();
        // 填写步骤1生成的访问密钥AccessKey ID和AccessKey Secret。
        String AccessKeyId = aliyunOssConfig.getRamAccessKeyId();
        String accessKeySecret = aliyunOssConfig.getRamAccessKeySecret();
        // 填写步骤3获取的角色ARN。
        String roleArn = aliyunOssConfig.getRamRoleArn();
        // 自定义角色会话名称,用来区分不同的令牌,例如可填写为SessionTest。
        String roleSessionName = userName;
        // 以下Policy用于限制仅允许使用临时访问凭证向目标存储空间examplebucket上传文件。
        // 临时访问凭证最后获得的权限是步骤4设置的角色权限和该Policy设置权限的交集,即仅允许将文件上传至目标存储空间examplebucket下的exampledir目录。
//        String policy = "{" +
//                "    \"Version\": \"1\", " +
//                "    \"Statement\": [" +
//                "        {\n" +
//                "            \"Action\": [" +
//                "                \"oss:PutObject\"" +
//                "            ]," +
//                "            \"Resource\": [" +
//                "                \"acs:oss:*:*:com-seaurl-cdn/space\"," +
//                "                \"acs:oss:*:*:com-seaurl-cdn/space/*\"," +
//                "            ]," +
//                "            \"Effect\": \"Allow\"" +
//                "        }" +
//                "    ]" +
//                "}";
        try {
            // regionId表示RAM的地域ID。以华东1(杭州)地域为例,regionID填写为cn-hangzhou。也可以保留默认值,默认值为空字符串("")。
            String regionId = aliyunOssConfig.getRegion();
            // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
            DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
            // 添加endpoint。适用于Java SDK 3.12.0以下版本。
            // DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
            // 构造default profile。
            IClientProfile profile = DefaultProfile.getProfile(regionId, AccessKeyId, accessKeySecret);
            // 构造client。
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            // 适用于Java SDK 3.12.0及以上版本。
            request.setSysMethod(MethodType.POST);
            // 适用于Java SDK 3.12.0以下版本。
            //request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(null); // 如果policy为空,则用户将获得该角色下所有权限。
            request.setDurationSeconds(3600L); // 设置临时访问凭证的有效时间为3600秒。
            final AssumeRoleResponse response = client.getAcsResponse(request);
            System.out.println("Expiration: " + response.getCredentials().getExpiration());
            System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
            System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
            System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
            System.out.println("RequestId: " + response.getRequestId());
            return response;
        } catch (com.aliyuncs.exceptions.ClientException e) {
            System.out.println("Failed:");
            System.out.println("Error code: " + e.getErrCode());
            System.out.println("Error message: " + e.getErrMsg());
            System.out.println("RequestId: " + e.getRequestId());
            return null;
        }
    }

2、设置跨域支持
如果设置了跨域规则还是不起作用,可以使用下面方式进行修改:
设置跨域规则后调用OSS时仍然报“No 'Access-Control-Allow-Origin'”的错误

上传文件(简单上传和分片上传)

上面配置好权限之后,接下来我们来安装和使用

1、安装ali-oss

yarn add ali-oss

2、初始化oss client
注意:getAcsForUploadFile()方法就是上面获取sts token的请求方法

 //初始化oss client客户端
  async function initOssClient() {
    let res = await dispatch(getAcsForUploadFile())
    console.log('initOss=', res)
    if (res.payload.status === 0) {
      const resData = res.payload.data
      const client = new OSS({
        secure: true,
        // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
        region: 'oss-cn-hangzhou',
        // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
        accessKeyId: resData.credentials.accessKeyId,
        accessKeySecret: resData.credentials.accessKeySecret,
        // 从STS服务获取的安全令牌(SecurityToken)。
        stsToken: resData.credentials.securityToken,
        refreshSTSToken: async () => {
          // 向您搭建的STS服务获取临时访问凭证。
          const refreshRes = await dispatch(getAcsForUploadFile())
          console.log('refreshSTSToken info=', refreshRes)
          if (refreshRes.payload.status === 0) {
            const refreshResData = refreshRes.payload.data
            return {
              accessKeyId: refreshResData.credentials.accessKeyId,
              accessKeySecret: refreshResData.credentials.accessKeySecret,
              stsToken: refreshResData.credentials.securityToken
            }
          }

        },
        // 刷新临时访问凭证的时间间隔,单位为毫秒。
        refreshSTSTokenInterval: 3000000,
        // 填写Bucket名称。
        bucket: 'xxx'
      });
      if (client) {
        setOssClient(client)
      }
    }
  }

3、上传文件

 // 上传文件前判断单个文件是否超过500mb,如果超过不允许上传
  function beforeUploadFile(file, fileList) {
    console.log('file=', file)
    const isLt500M = file.size / 1024 / 1024 > 500;
    if (isLt500M) {
      message.warning('超过最大文件上传大小500Mb');
      return false
    }
    return true; // 不调用默认的上传方法
  }

 // 上传文件状态回调
  function onUploadFileChange(info) {
    console.log('info=', info)
    // 把没有status的文件过滤掉(没有status的文件说明是取消上传的文件)
    if (info.file.status) {
      setFileTaskDlg({
        show: true,
        task: info
      })
      if (info.file.status === 'done') {
        dispatch(getFileListByCid({
          cid: query.cid
        }))
      }
      if (info.file.status === 'error') {
        dispatch(getFileListByCid({
          cid: query.cid
        }))
      }
    }
  }

// 自定义上传
  function uploadFileToOss(options) {
    console.log('options=', options)
    const {file, onProgress, onSuccess, onError} = options

    return new Promise(async (resolve, reject) => {
      const filePath = `space/file/${me.name}/${file.name}`
      const fileType = getFileType(file.name)
      let result = null;
      // 判断文件是否超过100mb,如果超过则使用分片上传,否则使用简单上传
      if (file.size / 1024 / 1024 > 100) {
        console.log('分片上传')
        // 分片上传,默认分片1Mb,如果想更改,请参考:https://help.aliyun.com/document_detail/383952.html
        result = await ossClient.multipartUpload(filePath, file, {
          progress: async (percent) => {
            onProgress({percent: percent * 100})
          },
        }).catch((error) => {
          onError(error)
          reject(error)
        })
      } else {
        console.log('简单上传')
        // 简单上传
        result = await ossClient.put(filePath, file, {
          progress: async (percent) => {
            onProgress({percent: percent * 100})
          },
        }).catch((error) => {
          onError(error)
          reject(error)
        })
      }
      await uploadFileCallback(result, {
        file,
        filePath,
        fileType
      }, (res) => {
        onSuccess(res)
        resolve(result)
      })
    })
  }

  async function uploadFileCallback(result, data, callback) {
    const ossPath = result.res.requestUrls[0].split('?')[0]
    const url = ossPath.replace(/^http:\/\//i, 'https://')
    const newFileRes = {
      status: result?.res?.status,
      name: result?.name,
      url: url,
    }
    // 上传成功之后往后台数据库插入一条文件数据
    await dispatch(addFile({
      fileName: data.file.name,
      filePath: data.filePath,
      fileSize: data.file.size,
      fileContentType: data.fileType,
      categoryId: query.cid,
    }))
    callback(newFileRes)
  }


...

  

beforeUploadFile()方法是在上传文件之前做自己的业务逻辑处理,比如我这里的超过500mb的文件不允许上传

uploadFileToOss()Upload组件自定义上传规则,所以你不用设置action等属性

onUploadFileChange()方法是上传文件时的变化回调,可以通过这个方法设置显示文件上传动态

下载文件(批量下载)

下载文件跟上传差不多,下面示例

function onDownload(d) {
    
    if (selectedRowKeys.length <= 0) {
      message.warning('请先选择文件');
    } else {
      console.log('selectedRowKeys=', selectedRowKeys)

      const filterFiles = files.filter(item => selectedRowKeys.includes(item.id))
      console.log('filterFiles=', filterFiles)
      filterFiles.forEach(item => {
        const fileName = item.name
        const response = {
          'content-disposition': `attachment; filename=${encodeURIComponent(fileName)}`
        }
        // 填写Object完整路径。Object完整路径中不能包含Bucket名称。
        const url = ossClient.signatureUrl(item.path, {response});
        const iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        iframe.style.height = 0;
        iframe.src = url;
        // 与前两种方式不同,iframe需要挂载到页面上才能触发请求
        document.body.appendChild(iframe);
        setTimeout(() => {
          iframe.remove();
        }, 1000);
      })
    }
  }

说明:selectedRowKeys是我的文件列表选择的文件ids集合
item.path是文件在oss的完整路径,注意:不包含host和bucket-name,如:a/b/c.txt
image.png

总结

1、阿里云oss上传文件一开始我使用的是java sdk上传,发现不能在web端分片,于是使用了ali-oss组件包在浏览器端上传
2、要配置ram权限以及获取sts token还有跨域

引用

js实现单个文件下载,批量下载多个文件
async/await 捕获错误 捕获catch
OSS + Ant Design Upload 网页直传
OSS文档:预览或下载文件
OSS文档

你可能感兴趣的:(阿里云oss)