背景

本人日常工作中使用比较多的就是对象存储,用到过阿里云的、腾讯云的、字节云的等等,每家的对象存储协议都大同小异,并且都支持AmazonS3协议;所以只要吃透AmazonS3,基本全部可以搞定,在使用AmazonS3进行处理过程中存在一个需求:

如何实现AmazonS3支持客户端SDK进行两个跨域桶之间的文件拷贝?

可能有的同学知道:官方文档有提供跨域拷贝的ACL命令,何必自己在客户端捣鼓呢?

其实还是业务场景问题,客户端更加灵活,方便业务操作处理,不一定只是单出的CV操作,但是目前的AmazonS3只能支持先下载,后上传,存在落盘的额外时间,性能非常慢,故需要想办法进行优化,怎么可以做到直接的内存转发拷贝呢?

问题分析解决

【不支持原因分析】

经过研究发现,S3是通过http协议获取流进行交互的,流本身不支持mark的;但是S3在进行数据上传时需要进行签名验证,签名过程会读取流数据,然后签名ok后才进行上传,此时会发生reset操作;由此导致下载流不能支持直接上传。

【支持方案】

基于S3的签名后上传的特性,触发reset时必定是流读完重置到pos=0,那么为了实现下载流支持reset,可以通过FilterInputStream进行封装重写reset方法,触发时,关闭当前流,重新建立一个下载流,如下图

S3Object s3Object = amazonS3.getObject(srcBucket, srcKey);
ObjectMetadata metadata = s3Object.getObjectMetadata();
 
InputStream inp = new FilterInputStream(s3Object.getObjectContent()) {
    public boolean markSupported() {
        return true;
    }
    public synchronized void reset() throws IOException {
        this.close();
        S3Object newObj = amazonS3.getObject(srcBucket, srcKey);
        this.in = newObj.getObjectContent();
    }
};
 
 
PutObjectRequest putObjectRequest = new PutObjectRequest(descBucket, descKey, inp, metadata);
int readLimitInBytes = 1024 * 1024 * 100;
putObjectRequest.getRequestClientOptions().setReadLimit(readLimitInBytes);
amazonS3.putObject(putObjectRequest);

方案对比

操作方案 优点 缺点 图示
落盘后上传 1、仅一次网络下载
2、内存可控
3、支持大文件
1、多了1次写盘和2次读盘
2、磁盘IO影响转发速度
下图:落盘方案
流式不落盘上传 1、支持大文件
2、不用落地
3、内存可控
4、转发速度快
1、占用双倍的下载流量
2、容易受到网络干扰导致转移失败
下图:流处理方案
byte数组中转 1、仅一次网络下载
2、转发速度快
3、不用落地
1、最大仅支持4G以下的文件
2、内存不可控,需要考虑服务内存限制
下图:byte数组方案
落盘方案

流处理方案

byte数组方案

建议根据实际情况,配合使用以上三种改方案。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐