默认情况下,如果新添加文件与现有文件(Object)同名且对该文件有访问权限,则新添加的文件将覆盖原有的文件。本文介绍如何通过设置请求头x-oss-forbid-overwrite在简单上传、拷贝文件及分片上传等场景中禁止覆盖同名文件。

简单上传

以下代码用于简单上传时禁止覆盖同名文件:

// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
String accessKeyId = "<yourAccessKeyId>";
String accessKeySecret = "<yourAccessKeySecret>";
String bucketName = "<yourBucketName>";
String objectName = "<yourObjectName>";
String content = "Hello OSS!";

OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));

// 指定上传文件操作时是否覆盖同名Object。
// 不指定x-oss-forbid-overwrite时,默认覆盖同名Object。
// 指定x-oss-forbid-overwrite为false时,表示允许覆盖同名Object。
// 指定x-oss-forbid-overwrite为true时,表示禁止覆盖同名Object,如果同名Object已存在,程序将报错。
ObjectMetadata metadata = new ObjectMetadata();
metadata.setHeader("x-oss-forbid-overwrite", "true");
putObjectRequest.setMetadata(metadata);

// 上传文件。
ossClient.putObject(putObjectRequest);

// 关闭OSSClient。
ossClient.shutdown();

简单上传的更多详情,请参见PutObject

拷贝文件

  • 通过CopyObjectRequest拷贝

    以下代码用于通过CopyObjectRequest拷贝小文件时禁止覆盖同名文件:

    // Endpoint以杭州为例,其它Region请按实际情况填写。
    String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
    // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
    String accessKeyId = "<yourAccessKeyId>";
    String accessKeySecret = "<yourAccessKeySecret>";
    
    String sourceBucketName = "<yourSourceBucketName>";
    String sourceObjectName = "<yourSourceObjectName>";
    String destinationBucketName = "<yourDestinationBucketName>";
    String destinationObjectName = "<yourDestinationObjectName>";
    
    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    
    // 创建CopyObjectRequest对象。
    CopyObjectRequest copyObjectRequest = new CopyObjectRequest(sourceBucketName, sourceObjectName, destinationBucketName, destinationObjectName);
    
    // 设置新的文件元信息。
    ObjectMetadata meta = new ObjectMetadata();
    meta.setContentType("text/html");
    // 指定拷贝文件操作时是否覆盖同名Object。
    // 不指定x-oss-forbid-overwrite时,默认覆盖同名Object
    // 指定x-oss-forbid-overwrite为false时,表示允许覆盖同名Object。
    // 指定x-oss-forbid-overwrite为true时,表示禁止覆盖同名Object,如果同名Object已存在,程序将报错。
    metadata.setHeader("x-oss-forbid-overwrite", "true");
    copyObjectRequest.setNewObjectMetadata(meta);
    
    // 拷贝文件。
    CopyObjectResult result = ossClient.copyObject(copyObjectRequest);
    System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
    
    // 关闭OSSClient。
    ossClient.shutdown();
  • 拷贝大文件

    以下代码用于拷贝大文件(分片拷贝)时禁止覆盖同名文件:

    // Endpoint以杭州为例,其它Region请按实际情况填写。
    String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
    // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
    String accessKeyId = "<yourAccessKeyId>";
    String accessKeySecret = "<yourAccessKeySecret>";
    
    String sourceBucketName = "<yourSourceBucketName>";
    String sourceObjectName = "<yourSourceObjectName>";
    String destinationBucketName = "<yourDestinationBucketName>";
    String destinationObjectName = "<yourDestinationObjectName>";
    
    ObjectMetadata objectMetadata = ossClient.getObjectMetadata(sourceBucketName, sourceObjectName);
    // 获取被拷贝文件的大小。
    long contentLength = objectMetadata.getContentLength();
    
    // 设置分片大小为10MB。
    long partSize = 1024 * 1024 * 10;
    
    // 计算分片总数。
    int partCount = (int) (contentLength / partSize);
    if (contentLength % partSize != 0) {
        partCount++;
    }
    System.out.println("total part count:" + partCount);
    
    // 初始化拷贝任务。可以通过InitiateMultipartUploadRequest指定目标文件元信息。
    InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(destinationBucketName, destinationObjectName);
    // 指定拷贝文件操作时是否覆盖同名Object。
    // 不指定x-oss-forbid-overwrite时,默认覆盖同名Object。
    // 指定x-oss-forbid-overwrite为false时,表示允许覆盖同名Object。
    // 指定x-oss-forbid-overwrite为true时,表示禁止覆盖同名Object,如果同名Object已存在,程序将报错。
    ObjectMetadata metadata = new ObjectMetadata();
    metadata.setHeader("x-oss-forbid-overwrite", "true");
    initiateMultipartUploadRequest.setObjectMetadata(metadata);
    
    InitiateMultipartUploadResult initiateMultipartUploadResult = ossClient.initiateMultipartUpload(initiateMultipartUploadRequest);
    String uploadId = initiateMultipartUploadResult.getUploadId();
    
    // 分片拷贝。
    List<PartETag> partETags = new ArrayList<PartETag>();
    for (int i = 0; i < partCount; i++) {
         // 计算每个分片的大小。
        long skipBytes = partSize * i;
        long size = partSize < contentLength - skipBytes ? partSize : contentLength - skipBytes;
    
        // 创建UploadPartCopyRequest。可以通过UploadPartCopyRequest指定限定条件。
        UploadPartCopyRequest uploadPartCopyRequest =
            new UploadPartCopyRequest(sourceBucketName, sourceObjectName, destinationBucketName, destinationObjectName);
        uploadPartCopyRequest.setUploadId(uploadId);
        uploadPartCopyRequest.setPartSize(size);
        uploadPartCopyRequest.setBeginIndex(skipBytes);
        uploadPartCopyRequest.setPartNumber(i + 1);
        UploadPartCopyResult uploadPartCopyResult = ossClient.uploadPartCopy(uploadPartCopyRequest);
    
        // 将返回的分片ETag保存到partETags中。
        partETags.add(uploadPartCopyResult.getPartETag());
    }
    
    // 完成分片拷贝任务。
    CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
                        destinationBucketName, destinationObjectName, uploadId, partETags);
    
    // 指定拷贝文件操作时是否覆盖同名Object。
    // 不指定x-oss-forbid-overwrite时,默认覆盖同名Object
    // 指定x-oss-forbid-overwrite为false时,表示允许覆盖同名Object。
    // 指定x-oss-forbid-overwrite为true时,表示禁止覆盖同名Object,如果同名Object已存在,程序将报错。
    completeMultipartUploadRequest.addHeader("x-oss-forbid-overwrite", "true");
    
    ossClient.completeMultipartUpload(completeMultipartUploadRequest);
    
    // 关闭OSSClient。
    ossClient.shutdown();

拷贝文件的更多详情,请参见CopyObject

分片上传

以下代码用于分片上传时禁止覆盖同名文件:

// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
String accessKeyId = "<yourAccessKeyId>";
String accessKeySecret = "<yourAccessKeySecret>";
String bucketName = "<yourBucketName>";
String objectName = "<yourObjectName>";

// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

// 创建InitiateMultipartUploadRequest对象。
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);

// 指定分片上传操作时是否覆盖同名Object。
// 不指定x-oss-forbid-overwrite时,默认覆盖同名Object。
// 指定x-oss-forbid-overwrite为false时,表示允许覆盖同名Object。
// 指定x-oss-forbid-overwrite为true时,表示禁止覆盖同名Object,如果同名Object已存在,程序将报错。
ObjectMetadata metadata = new ObjectMetadata();
metadata.setHeader("x-oss-forbid-overwrite", "true");
request.setObjectMetadata(metadata);

// 初始化分片。
InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
// 返回uploadId,它是分片上传事件的唯一标识,您可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
String uploadId = upresult.getUploadId();

// partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
List<PartETag> partETags =  new ArrayList<PartETag>();
// 计算文件有多少个分片。
final long partSize = 1 * 1024 * 1024L;   // 1MB
final File sampleFile = new File("<localFile>");
long fileLength = sampleFile.length();
int partCount = (int) (fileLength / partSize);
if (fileLength % partSize != 0) {
    partCount++;
 }
// 遍历分片上传。
for (int i = 0; i < partCount; i++) {
    long startPos = i * partSize;
    long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
    InputStream instream = new FileInputStream(sampleFile);
    // 跳过已经上传的分片。
    instream.skip(startPos);
    UploadPartRequest uploadPartRequest = new UploadPartRequest();
    uploadPartRequest.setBucketName(bucketName);
    uploadPartRequest.setKey(objectName);
    uploadPartRequest.setUploadId(uploadId);
    uploadPartRequest.setInputStream(instream);
    // 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100KB。
    uploadPartRequest.setPartSize(curPartSize);
    // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出这个范围,OSS将返回InvalidArgument的错误码。
    uploadPartRequest.setPartNumber( i + 1);
    // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
    UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
    // 每次上传分片之后,OSS的返回结果会包含一个PartETag。PartETag将被保存到partETags中。
    partETags.add(uploadPartResult.getPartETag());
}


// 创建CompleteMultipartUploadRequest对象。
// 完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
CompleteMultipartUploadRequest completeMultipartUploadRequest =
        new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);

// 指定分片上传操作时是否覆盖同名Object。
// 不指定x-oss-forbid-overwrite时,默认覆盖同名Object。
// 指定x-oss-forbid-overwrite为false时,表示允许覆盖同名Object。
// 指定x-oss-forbid-overwrite为true时,表示禁止覆盖同名Object,如果同名Object已存在,程序将报错。
completeMultipartUploadRequest.addHeader("x-oss-forbid-overwrite", "true");

// 完成上传。
CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);

// 关闭OSSClient。
ossClient.shutdown();

分片上传的更多详情,请参见InitiateMultipartUpload以及CompleteMultipartUpload