Skip to content
大纲

拷贝文件

拷贝一个文件

用户可以通过copyObject方法拷贝一个Object,如下代码所示:

java
public void copyObject(FosClient client, String srcBucketName, String srcKey, String destBucketName, String destKey) {
    // 拷贝Object
    CopyObjectResponse copyObjectResponse = client.copyObject(srcBucketName, srcKey, destBucketName, destKey);
    // 打印结果
    System.out.println("ETag: " + copyObjectResponse.getETag() + " LastModified: " + copyObjectResponse.getLastModified());
}

copyObject 方法返回一个 CopyObjectResponse 对象,该对象中包含了新Object的ETag和修改时间。

通过CopyObjectRequest拷贝Object

用户也可以通过 CopyObjectRequest 实现Object的拷贝,如下代码所示:

java
// 初始化FosClient
FosClient client = ...;

// 创建CopyObjectRequest对象
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketName, srcKey, destBucketName, destKey);

// 设置新的Metadata
Map<String, String> userMetadata = new HashMap<String, String>();
userMetadata.put('<user-meta-key>','<user-meta-value>');
ObjectMetadata meta = new ObjectMetadata();
meta.setUserMetadata(userMetadata);
copyObjectRequest.setNewObjectMetadata(meta);

// 复制Object
CopyObjectResponse copyObjectResponse = client.copyObject(copyObjectRequest);
System.out.println("ETag: " + copyObjectResponse.getETag() + " LastModified: " + copyObjectResponse.getLastModified());

CopyObjectRequest 允许用户修改目的Object的ObjectMeta,同时也提供 MatchingETagConstraints 参数的设定。 设置Object的Copy属性 FOS同时会提供CopyObject接口用于将一个已经存在的Object拷贝到另外一个Object,拷贝过程中会对源Object的Etag或修改状态进行判断,根据判断结果决定是否执行拷贝。详细的参数解释如下:

名称类型描述是否必需
x-fos-copy-source-if-matchString如果源Object的ETag值和用户提供的ETag相等,则执行拷贝操作,否则拷贝失败。
x-fos-copy-source-if-none-matchString如果源Object的ETag和用户提供的ETag不相等,则执行拷贝操作,否则拷贝失败。
x-fos-copy-source-if-unmodified-sinceString如果源object在x-fos-copy-source-if-unmodified-since之后没被修改,则执行拷贝操作,否则拷贝失败。
x-fos-copy-source-if-modified-sinceString如果源object在x-fos-copy-source-if-modified-since之后被修改了,则执行拷贝操作,否则拷贝失败。

对应的示例代码:

java
// 初始化FosClient
FosClient client = ...;
// 创建CopyObjectRequest对象
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketName, srcKey, destBucketName, destKey);
// 设置新的Metadata
Map<String, String> userMetadata = new HashMap<String, String>();
userMetadata.put("<user-meta-key>","<user-meta-value>");

meta.setUserMetadata(userMetadata);
copyObjectRequest.setNewObjectMetadata(meta);
//copy-source-if-match
copyObjectRequest.withETag("111111111183bf192b57a4afc76fa632");
//copy-source-if-none-match
copyObjectRequest.withNoMatchingETagConstraint("111111111183bf192b57a4afc76fa632");
Date modifiedSinceConstraint = new Date();
SimpleDateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.UK);
df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
String date = df.format(modifiedSinceConstraint);
//copy-source-if-modified-since
copyObjectRequest.withModifiedSinceConstraint(date);
//copy-source-if-unmodified-since
copyObjectRequest.withUnmodifiedSinceConstraint(date);
// 复制Object
CopyObjectResponse copyObjectResponse = client.copyObject(copyObjectRequest);
System.out.println("ETag: " + copyObjectResponse.getETag() + " LastModified: " + copyObjectResponse.getLastModified());

同步Copy功能 当前FOS的CopyObject接口是通过同步方式实现的。同步方式下,FOS端会等待Copy实际完成才返回成功。同步Copy能帮助用户更准确的判断Copy状态,但用户感知的复制时间会变长,且复制时间和文件大小成正比。 同步Copy方式更符合业界常规,提升了与其它平台的兼容性。同步Copy方式还简化了FOS服务端的业务逻辑,提高了服务效率。

分块拷贝

除了通过CopyObject接⼝拷贝文件以外,FOS还提供了另外一种拷贝模式——Multipart Upload Copy。用户可以在如下的应用场景内(但不仅限于此),使用Multipart Upload Copy,如:

  • 需要支持断点拷贝。
  • 拷贝超过5GB大小的文件。
  • 网络条件较差,和FOS的服务器之间的连接经常断开。

下面将介绍分步实现三步拷贝。 三步拷贝包含init、“拷贝分块”和complete三步,其中init和complete的操作同分块上传一致。 为了便于理解,下面提供三步拷贝完整代码:

java
// 第一步 init
InitiateMultipartUploadRequest initiateMultipartUploadRequest =
    new InitiateMultipartUploadRequest("targetBucketName","targetObjectName");
InitiateMultipartUploadResponse initiateMultipartUploadResponse =
    client.initiateMultipartUpload(initiateMultipartUploadRequest);

// 第二步 分块拷贝
long left_size=client.getObjectMetadata("sourceBucketName","sourceObjectName").getContentLength();
long skipBytes = 0;
int partNumber = 1;
List<PartETag> partETags = new ArrayList<PartETag>();

while (left_size > 0) {
    long partSize = 1024 * 1024 * 1L;
    if (left_size < partSize) {
        partSize = left_size;
    }
    UploadPartCopyRequest uploadPartCopyRequest = new UploadPartCopyRequest();
    uploadPartCopyRequest.setBucketName("targetBucketName");
    uploadPartCopyRequest.setKey("targetObjectName");
    uploadPartCopyRequest.setSourceBucketName("sourceBucketName");
    uploadPartCopyRequest.setSourceKey("sourceObjectName");
    uploadPartCopyRequest.setUploadId(initiateMultipartUploadResponse.getUploadId());
    uploadPartCopyRequest.setPartSize(partSize);
    uploadPartCopyRequest.setOffSet(skipBytes);
    uploadPartCopyRequest.setPartNumber(partNumber);
    UploadPartCopyResponse uploadPartCopyResponse = client.uploadPartCopy(uploadPartCopyRequest);
    // 将返回的PartETag保存到List中
    PartETag partETag = new PartETag(partNumber,uploadPartCopyResponse.getETag());
    partETags.add(partETag);
    left_size -= partSize;
    skipBytes += partSize;
    partNumber+=1;
}

// 第三步 complete
CompleteMultipartUploadRequest completeMultipartUploadRequest =
    new CompleteMultipartUploadRequest("targetBucketName", "targetObjectName", initiateMultipartUploadResponse.getUploadId(), partETags);
CompleteMultipartUploadResponse completeMultipartUploadResponse =
        client.completeMultipartUpload(completeMultipartUploadRequest);

注意:

  1. offset参数以字节为单位,为分块的开始偏移位置。
  2. size参数以字节为单位,定义每个分块的大小,除最后一个Part以外,其他的Part大小都要大于 5MB。