OSS 直链下载与PDF在线预览

1,329 阅读5分钟

一、使用背景

  • 开发的软件如果附件存储在 OSS 对象服务器中,在我们获取附件的时候往往都会通过先从数据库查询附件文件的ID再通过路径去调用 OSS 下载方法下载到本地之后再进行下载下来,这样一来往往会存在问题,第一如果文件过大那么下载往往都会很慢,第二如果网络带宽不大那么下载的速度也会大打折扣。
  • 通过阅读 OSS 对象存储官方文档发现,OSS 支持授权第三方下载,也就是通过在下载URL 地址内包含授权签名实现浏览器直链下载,这样下载的方式是通过 OSS内网进行下载避免了网络带宽过小带来的下载速度过慢的问题。
  • 通过阅读后发现由于STS 鉴权系统的默认实效性是15分钟,所以我们的URL下载实效性也得最少设置为15分钟,避免由于时效性失效带来的种种问题。

OSS可以通过阿里云STS(Security Token Service)进行临时授权访问。阿里云STS是为云计算用户提供临时访问令牌的Web服务。通过STS,您可以为第三方应用或子用户(即用户身份由您自己管理的用户)颁发一个自定义时效和权限的访问凭证。关于STS的更多信息,请参见STS介绍

二、使用STS临时访问凭证访问OSS

具体参考官方文档:help.aliyun.com/document_de…

步骤一:创建RAM用户

  1. 登录RAM控制台
  2. 在左侧导航栏,选择身份管理>用户。
  3. 单击创建用户。
  4. 输入登录名称和显示名称。
  5. 在访问方式区域下,选择Open API 调用访问,然后单击确定。
  6. 单击复制,保存访问密钥(AccessKey ID 和 AccessKey Secret)。

步骤二:为RAM用户授予请求AssumeRole的权限

  1. 单击已创建RAM用户右侧对应的添加权限。
  2. 在添加权限页面,选择AliyunSTSAssumeRoleAccess系统策略。
  3. 单击确定。

步骤三:创建用于获取临时访问凭证的角色

  1. 在左侧导航栏,选择身份管理>角色。
  2. 单击创建角色,选择可信实体类型为阿里云账号,单击下一步。
  3. 角色名称填写为RamOssTest,选择云账号为当前云账号。
  4. 单击完成。角色创建完成后,单击关闭。
  5. 在RAM角色管理页面,搜索框输入角色名称RamOssTest。
  6. 单击复制,保存角色的ARN。

步骤四:为角色授予下载文件的权限

  1. 创建上传文件的自定义权限策略。

  2. 在左侧导航栏,选择权限管理>权限策略。

  3. 单击创建权限策略。

  4. 在创建权限策略页面,单击脚本编辑,然后在策略文档输入框中赋予角色向目标存储空间examplebucket下的目录exampledir上传文件的权限。具体配置示例如下。

  5. 策略配置完成后,单击下一步。

  6. 在基本信息区域,填写策略名称为RamTestPolicy,然后单击确定

  7. 为RAM角色RamOssTest授予自定义权限策略。

    1. 在左侧导航栏,选择身份管理>角色。
    2. 在角色页面,找到目标RAM角色RamOssTest。
    3. 单击RAM角色RamOssTest右侧的添加权限。
    4. 在添加权限页面下的自定义策略页签,选择已创建的自定义权限策略RamTestPolicy。
    5. 单击确定。

三、使用方式

  • 在yml文件中定义 OSS 所需参数

    oss:
    endPoint: yourEndpoint accessKeyId: yourAccessKeyId accessKeySecret: yourAccessKeySecret
    bucketName: examplebucket

  • 在yml文件中定义 STS 所需参数

    sts: endPoint: yourStsEndpoint ossEndPoint: yourOssEndpint accessKeyId: yourAccessKeyId accessKeySecret: yourAccessKeySecret roleSessionName: stsSession roleArn: 上面步骤三复制的内容 regionId: yourRegionId

  • 鉴权方法

    /** * 鉴权 * @return */ private static void authentication() { String ossAuthInfo = CacheUtils.getValueWithRealKey(OSS_AUTH_REDIS_KEY); // 避免多次重复调用鉴权服务,故存储Redis if (Objects.nonNull(ossAuthInfo)) { String[] strings = ossAuthInfo.split(SEPARATE); ossAccessKeyId = strings[0]; ossAccessKeySecret = strings[1]; securityToken = strings[2]; return; } AssumeRoleResponse response; // 构造default profile。 IClientProfile profile = DefaultProfile.getProfile(regionId, stsAccessKeyId, stsAccessKeySecret); // 构造client。 DefaultAcsClient client = new DefaultAcsClient(profile); try { DefaultProfile.addEndpoint(regionId, "Sts", stsEndPoint); final AssumeRoleRequest request = new AssumeRoleRequest(); request.setSysMethod(MethodType.POST); request.setRoleArn(roleArn); request.setRoleSessionName(roleSessionName); // 设置临时访问凭证的有效时间为15分钟 request.setDurationSeconds(900L); response = client.getAcsResponse(request); ossAccessKeyId = response.getCredentials().getAccessKeyId(); ossAccessKeySecret = response.getCredentials().getAccessKeySecret(); securityToken = response.getCredentials().getSecurityToken(); String concatStr = ossAccessKeyId.concat(SEPARATE) .concat(ossAccessKeySecret) .concat(SEPARATE).concat(securityToken); CacheUtils.cacheValueWithExpireTime(OSS_AUTH_REDIS_KEY, 900L, concatStr); } catch (com.aliyuncs.exceptions.ClientException e) { logger.error("鉴权服务连接失败", e); }finally { if (Objects.nonNull(client)) { client.shutdown(); } } }

  • 生成多个访问下载链接地址-PDF可下载

    /** * 生成多个以GET方法访问的签名URL-PDF可下载 * @return */ public static Map<Long, URL> getDownloadUrl(List fileIds) { Map<Long, URL> urlMap = new HashMap<>(fileIds.size()); // 根据文件id获取文件下载全路径 ResultData<List> resultData = getFileInfo(fileIds); List fileVo = new ArrayList<>(fileIds.size()); if (Objects.equals("1", resultData.getCode())) { fileVo.addAll(resultData.getData()); } // 获取securityToken authentication(); // 查找到文件 if (CollectionUtils.isNotEmpty(baseFileOutputVos)) { // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(ossEndPoint, ossAccessKeyId, ossAccessKeySecret, securityToken); // 处理文件名 Map<Long, String> fileName = fileVo.stream() .collect(Collectors.toMap(FileVo::getId, FileVo::getFileName)); try { // 设置签名URL过期时间,单位为毫秒。 Date expiration = new Date(System.currentTimeMillis() + 900 * 1000); ResponseHeaderOverrides responseHeaderOverrides = new ResponseHeaderOverrides(); / // 这个很重要,具体参考官网 responseHeaderOverrides.setContentType("application/octet-stream"); fileVo.forEach(v -> { GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, v.getFilePath()); // 设置过期时间 generatePresignedUrlRequest.setExpiration(expiration); responseHeaderOverrides.setContentDisposition("attachment; filename=" + fileName.get(v.getId())); generatePresignedUrlRequest.setResponseHeaders(responseHeaderOverrides); URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest); urlMap.put(v.getId(), url); }); } catch (OSSException oe) { logger.error("OSS 服务出错", oe); } catch (ClientException ce) { logger.error("OSS 服务连接出错", ce); }finally { if (Objects.nonNull(ossClient)) { ossClient.shutdown(); } } } return urlMap; }

  • 生成多个访问下载链接地址-PDF可预览

    /** * 生成多个以GET方法访问的签名URL-PDF直接预览 * @return */ public static Map<Long, URL> getPreviewUrl(List fileIds) { Map<Long, URL> urlMap = new HashMap<>(fileIds.size()); // 根据文件id获取文件下载全路径 ResultData<List> resultData = getFileInfo(fileIds); List fileVo = new ArrayList<>(fileIds.size()); if (Objects.equals("1", resultData.getCode())) { fileVo.addAll(resultData.getData()); } // 获取securityToken authentication(); // 查找到文件 if (CollectionUtils.isNotEmpty(fileVo)) { // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(ossEndPoint, ossAccessKeyId, ossAccessKeySecret, securityToken); try { // 设置签名URL过期时间,单位为毫秒。 Date expiration = new Date(System.currentTimeMillis() + 900 * 1000); fileVo.forEach(v -> { URL url = ossClient.generatePresignedUrl(bucketName, v.getFilePath(), expiration); urlMap.put(v.getId(), url); }); } catch (OSSException oe) { logger.error("OSS 服务出错", oe); } catch (ClientException ce) { logger.error("OSS 服务连接出错", ce); }finally { if (Objects.nonNull(ossClient)) { ossClient.shutdown(); } } } return urlMap; }

以上是我在项目中使用 OSS 的总结,如有问题请指出, 谢谢!!!