在客户端直接上传文件到OSS

客户端直传是指客户端直接上传文件到对象存储OSS。相对于服务端代理上传,客户端直传避免了业务服务器中转文件,提高了上传速度,节省了服务器资源。本文介绍客户端直传的方案优势、安全实现和实践参考。

为什么客户端直传

在典型的服务端和客户端架构下,常见的文件上传方式是服务端代理上传:客户端将文件上传到业务服务器,然后业务服务器将文件上传到OSS。在这个过程中,一份数据需要在网络上传输两次,会造成网络资源的浪费,增加服务端的资源开销。为了解决这一问题,您可以在客户端直连OSS来完成文件上传,无需经过业务服务器中转。

如何实现客户端直传

实现客户端直传需要解决以下两个大问题:

跨域访问

如果您的客户端是Web端或小程序,您需要解决跨域访问被限制的问题。浏览器以及小程序容器出于安全考虑,通常都会限制跨域访问,这一限制也会限制您的客户端代码直连OSS。您可以通过配置OSS Bucket的跨域访问规则,来允许指定域名下的Web应用或小程序直接访问OSS。更多信息,请参见跨域设置

安全授权

上传文件到OSS需要使用RAM用户的访问密钥(AccessKey)来完成签名认证,但是在客户端中使用长期有效的访问密钥,可能会导致访问密钥泄露,进而引起安全问题。为了解决这一问题,如下是一个方案实现安全上传:

服务端生成PutObject所需的签名****URL

对于简单上传文件的场景,您可以在服务端使用OSS SDK生成PutObject所需的签名URL,客户端可以凭借签名URL,不依赖OSS SDK直接上传文件。需要注意的是,此方案不适用于基于分片上传大文件、基于分片断点续传的场景。在服务端对每个分片生成签名URL,并将签名URL返回给客户端,会增加与服务端的交互次数和网络请求的复杂性。另外,客户端可能会修改分片的内容或顺序,导致最终合并的文件不正确。

服务端通过签名URL授权客户端上传文件到OSS的过程如下。

  1. 客户端向业务服务器请求签名URL。
  2. 业务服务器使用OSS SDK生成PUT类型的签名URL,然后将其返回给客户端。
  3. 客户端使用PUT类型的签名URL调用PutObject上传文件到OSS。
  4. OSS向客户端返回成功响应。

示例代码:

java 复制代码
    /**
     * 生成用于直传的 PUT 类型的预签名 URL。
     * 客户端必须在上传时设置与签名一致的 Content-Type。
     * 具体看 OSS 的官方文档,有详细的案例
     *
     * @param objectKey 目标对象键
     * @param contentType 上传内容类型(如 text/markdown, image/png)
     * @param expiresInSeconds 有效期秒数(建议 300-900)
     * @return 可直接用于 PUT 上传的预签名 URL
     */
    public String generatePresignedPutUrl(String objectKey, String contentType, int expiresInSeconds) {
        // 确保配置文件中,相关配置已正常配置,若未配置则抛出异常
        ensureConfigured();

        // 初始化 OSS 客户端实例
        OSS client = new OSSClientBuilder()
                .build(props.getEndpoint(), props.getAccessKeyId(), props.getAccessKeySecret());

        try {
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(props.getBucket(), objectKey, HttpMethod.PUT);

            // System.currentTimeMillis() → 返回当前时间距离 1970-01-01 00:00:00 UTC 的毫秒数(一个 long 类型的值)
            // expiresInSeconds * 1000L → 把秒数转换成毫秒数(因为时间戳是用毫秒计量的,所以要 ×1000)
            // 两者相加 → 得到未来某个时刻的时间戳(毫秒)
            // new Date(那个毫秒值) → 创建一个代表"那个未来时刻"的 Date 对象
            Date expiration = new Date(System.currentTimeMillis() + expiresInSeconds * 1000L);
            // 设置过期时间(文档中说,单位是毫秒)
            request.setExpiration(expiration);

            // 客户端必须在上传时设置与签名一致的 Content-Type
            if (contentType != null && !contentType.isBlank()) {
                request.setContentType(contentType);
            }

            // 生成用于直传的 PUT 类型的预签名 URL
            URL url = client.generatePresignedUrl(request);
            // 返回
            return url.toString();
        } finally {
            // 最后释放资源
            client.shutdown();
        }
    }
相关推荐
冉冰学姐9 小时前
基于ssm的技能比赛报名管理系统29817vn0(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
java·数据库·spring·ssm 框架应用
代码雕刻家11 小时前
3.5.Maven-依赖管理-依赖配置&依赖传递
java·maven
!chen11 小时前
MyBatis-plus拓展之字段类型处理器、自动填充和乐观锁
java·tomcat·mybatis
无限大612 小时前
《AI观,观AI》:善用AI赋能|让AI成为你深耕核心、推进重心的“最强助手”
前端·后端
uzong12 小时前
CoPaw是什么?-- 2026年开源的国产个人AI助手
人工智能·后端
Jin、yz12 小时前
JAVA 八股
java·开发语言
无心水12 小时前
【任务调度:框架】11、分布式任务调度进阶:高可用、幂等性、性能优化三板斧
人工智能·分布式·后端·性能优化·架构·2025博客之星·分布式调度框架
va学弟12 小时前
Java 网络通信编程(6):视频通话
java·服务器·网络·音视频
pjw1988090312 小时前
Spring Framework 中文官方文档
java·后端·spring
盒马盒马12 小时前
Rust:迭代器
开发语言·后端·rust