S3 Java SDK手册

安装

Java环境

JDK 6及以上版本

Maven项目中使用

在Maven工程中使用AWS S3 Java SDK只需在pom.xml中加入相应依赖即可

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-s3</artifactId>
    <version>1.11.125</version>
</dependency>
<!--为了支持加密-->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.55</version>
</dependency>

工程中直接引入jar

  • 下载包含AWS Java SDK及其依赖的开发包: aws-java-sdk.zip
  • 解压该开发包;
  • 将解压后文件夹下lib文件夹中aws-java-sdk-1.11.254.jar(版本可能不一样)的以及third-party/lib文件夹下的所有jar文件拷贝到您的项目中;
  • 在Eclipse中选择您的工程,右击 -> Properties -> Java Build Path -> Add JARs
  • 选中您在第三步拷贝的所有JAR文件
  • 在Intellij Idea中,选取所有的JAR文件,右击 -> Add As Library
  • 如果要使用客户端加密,需要加入 bcprov-jdk15on

前言

Java SDK提供的接口都在AmazonS3中实现,并以成员方法的方式对外提供调用。因此使用Java SDK前必须实例化一个AmazonS3对象。

关于请求参数

AmazonS3中的方法一般都提供两种参数传入方式:

  • 普通传参方式:

example:

AmazonS3.createBucket(bucketName);
  • request对象传参方式:

example:

CreateBucketRequest request = new CreateBucketRequest(bucketName);
request.setCannedAcl(CannedAccessControlList.Private);
AmazonS3.createBucket(request);

后面的使用指南只以其中的一种传参方式作为例子。

关于异常

所有错误异常包装在两个异常类型中,在调用Java SDK接口的时候,捕捉这些异常并打印必要的信息有利于定位问题(ps:在JAVA SDK使用指南的简单示例代码没有重复赘述,使用时注意添加)。

try{
    s3Client.XXX("XXX");
//捕捉NOS服务器异常错误
}catch (AmazonServiceException e1){
    System.out.println("Error Message:    " + e1.getMessage());    //错误描述信息
    System.out.println("HTTP Status Code: " + e1.getStatusCode()); //错误http状态码
    System.out.println("NOS Error Code:   " + e1.getErrorCode());  //服务器定义错误码
    System.out.println("Error Type:       " + e1.getErrorType());  //客户端的请求错误还是服务端的处理错误
    System.out.println("Request ID:       " + e1.getRequestId());  //请求ID,非常重要,有利于nos开发人员跟踪异常请求的错误原因
//捕捉客户端错误
}catch(SdkClientException e2){
    System.out.println("Error Message: " + e2.getMessage());       //客户端错误信息
}

初始化

  1. 确定Endpoint
目前有效的Endpoint为:nos-eastchina1.126.net
  1. 获取密钥对

使用AWS S3 Java SDK前,您需要拥有一个有效的 Access Key(包括Access Key和Access Secret)用来进行签名认证。可以通过如下步骤获得:

1)登录 https://c.163.com/ 注册用户

2)注册后,蜂巢会颁发 Access Key 和 Secret Key 给客户,您可以在蜂巢“用户中心”的“Access Key”查看并管理您的Access Key

  1. 在代码中实例化AmazonS3

S3推荐获取accessKey,secretKey的方式

 AWSCredentials credentials = null;
 try {
     credentials = new ProfileCredentialsProvider().getCredentials();
 } catch (Exception e) {
     throw new AmazonClientException(
             "Cannot load the credentials from the credential profiles file. " +
                     "Please make sure that your credentials file is at the correct " +
                     "location (~/.aws/credentials), and is in valid format.",
             e);
 }

/*
credentials文件的目录:~/.aws/credentials
credentials文件中的格式:
[default]
aws_access_key_id = xxxx
aws_secret_access_key = xxx
*/

推荐使用的方式

String accessKey = "your-accesskey";
String secretKey = "your-secretKey ";
AWSCredentials credentials = new BasicAWSCredentials(accessKey,secretKey);
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
            .withCredentials(new AWSStaticCredentialsProvider(credentials))
            .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("endpoint","region"))//endpoint,region请指定为NOS支持的(us-east-1:hz,us-east2:bj)
            .build();

不推荐使用的方式

String accessKey = "your-accesskey";
String secretKey = "your-secretKey ";
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
AmazonS3 s3Client = new AmazonS3Client(credentials);//该方法弃用,不推荐使用
s3Client.setEndpoint(endPoint);//由于s3的sdk默认使用的是s3的服务地址,所以如果要使用NOS的服务,必须显示指定Endpoint

注:AmazonS3是线程安全的,可以并发使用

  1. 配置AmazonS3

如果您需要修改AmazonS3的默认参数,可以在实例化AmazonS3时传入ClientConfiguration实例。ClientConfiguration是AmazonS3的配置类,可配置连接超时、最大连接数等参数。通过ClientConfiguration可以设置的参数见下表:

参数 描述 调用方法
connectionTimeout
建立连接的超时时间(单位:毫秒)
默认:50000毫秒
setConnectionTimeout
maxConnections
允许打开的最大HTTP连接数
默认:50
setMaxConnections
socketTimeout
Socket层传输数据超时时间(单位:毫秒)
默认:50000毫秒
setSocketTimeout
maxErrorRetry
请求失败后最大的重试次数
默认:3次
setMaxErrorRetry
protocol
使用http协议还是https协议
默认:https协议
setProtocol

带ClientConfiguration参数实例化AmazonS3的示例代码:

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

String accessKey = "your-accesskey";
String secretKey = "your-secretKey ";
Credentials credentials = new BasicCredentials(accessKey, secretKey);
ClientConfiguration conf = new ClientConfiguration();
// 设置AmazonS3使用的最大连接数
conf.setMaxConnections(200);
// 设置socket超时时间
conf.setSocketTimeout(10000);
// 设置失败请求重试次数
conf.setMaxErrorRetry(2);
// 如果要用https协议,请加上下面语句
conf.setProtocol(Protocol.HTTPS);

//AmazonS3 s3Client = new AmazonS3Client(credentials,clientConfiguration);
//s3Client.setEndpoint(endPoint);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
            .withCredentials(new AWSStaticCredentialsProvider(credentials))
            .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("endpoint,"region"))//endpoint,region请指定为NOS支持的
            .withClientConfiguration(conf)
            .build();

注意:后面的示例代码默认您已经实例化了所需的AmazonS3对象

  1. 在代码中实例化AmazonS3Encryption

可以把AmazonS3Encryption当做AmazonS3用,除了StrictAuthenticatedEncryption这个不支持range,且要求JDK必须在1.7及以上,由于NOS目前不支持KMS,所以用户的CMK只能自己管理

 String accessKey = "your-accesskey";
 String secretKey = "your-secretKey ";
//1.获取CMK,客户端主密钥,可以使用对称和分对称两种方式,下述使用的是非对称的
 KeyPair keyPair = loadKeyPair(keyDir,"RSA");

 // 2. Construct an instance of AmazonS3Encryption.
 EncryptionMaterials encryptionMaterials = new EncryptionMaterials(
         keyPair);
 ClientConfiguration configuration = new ClientConfiguration();
 configuration.setProtocol(Protocol.HTTPS);
 CryptoConfiguration cryptoConfiguration = new CryptoConfiguration();
 //支持EncryptionOnly,AuthenticatedEncryption,StrictAuthenticatedEncryption,默认是EncryptionOnly,StrictAuthenticatedEncryption不支持range请求
 cryptoConfiguration.setCryptoMode(CryptoMode.StrictAuthenticatedEncryption);
 //保存加密信息的方式,有两种方式,Instruction模式和Metadata模式,由于NOS分块上传和S3支持上存在一些差异,导致metadata保存的方式大文件下载时由于找不到加密信息而不解密
 cryptoConfiguration.setStorageMode(CryptoStorageMode.InstructionFile);

 EncryptionMaterialsProvider encryptionMaterialsProvider = new StaticEncryptionMaterialsProvider(encryptionMaterials);


 ClientConfiguration clientConfiguration = new ClientConfiguration();
 clientConfiguration.setProtocol(Protocol.HTTPS);
         AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration("nos-eastchina1.126.net","us-east-1");
 AmazonS3Encryption encryptionClient = AmazonS3EncryptionClientBuilder.standard().withCryptoConfiguration(cryptoConfiguration).
         withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey,secretKey)))
         .withEncryptionMaterials(encryptionMaterialsProvider).withClientConfiguration(clientConfiguration).withEndpointConfiguration(endpointConfiguration).build();


 //loadKeyPair的实现方式,非对称加密algorithm = RSA
 KeyPair loadKeyPair(String path, String algorithm)
         throws IOException, NoSuchAlgorithmException,
         InvalidKeySpecException {
     // read public key from file
     File filePublicKey = new File(path + "/public.key");
     FileInputStream fis = new FileInputStream(filePublicKey);
     byte[] encodedPublicKey = new byte[(int) filePublicKey.length()];
     fis.read(encodedPublicKey);
     fis.close();

     // read private key from file
     File filePrivateKey = new File(path + "/private.key");
     fis = new FileInputStream(filePrivateKey);
     byte[] encodedPrivateKey = new byte[(int) filePrivateKey.length()];
     fis.read(encodedPrivateKey);
     fis.close();

     // Convert them into KeyPair
     KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
     X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
             encodedPublicKey);
     PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

     PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
             encodedPrivateKey);
     PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

     return new KeyPair(publicKey, privateKey);
 }
 //对称加密,algorithm = AES
 SecretKey loadKeyPair(String path, String algorithm)
         throws IOException {
     //Read private key from file.
     File keyFile = new File(path);
     FileInputStream keyfis = new FileInputStream(keyFile);
     byte[] encodedPrivateKey = new byte[(int) keyFile.length()];
     keyfis.read(encodedPrivateKey);
     keyfis.close();
     //Generate secret key.
     return new SecretKeySpec(encodedPrivateKey, algorithm);
 }

 //生成Key的方式,非对称加密
 public static KeyPair genKeyPair(String algorithm, int bitLength)
         throws NoSuchAlgorithmException {
     KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(algorithm);
     SecureRandom srand = new SecureRandom();
     keyGenerator.initialize(bitLength, srand);
     return keyGenerator.generateKeyPair();
 }
 //生成Key的方式,对称加密
 SecretKey generateCMasterKey() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
     KeyGenerator symKeyGenerator = null;
     try {
         symKeyGenerator = KeyGenerator.getInstance("AES");
     } catch (NoSuchAlgorithmException e) {
         e.printStackTrace();
     }
     symKeyGenerator.init(256);
     return  symKeyGenerator.generateKey();
 }
 //保存Key,该key只要生成一次就好了,要妥善保管,如果该key丢失了,那么意味着通过该key加密的数据将没法解密
 //非对称
 public static void saveKeyPair(String dir, KeyPair keyPair)
         throws IOException {
     PrivateKey privateKey = keyPair.getPrivate();
     PublicKey publicKey = keyPair.getPublic();

     X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
             publicKey.getEncoded());
     FileOutputStream fos = new FileOutputStream(dir + "/public.key");
     fos.write(x509EncodedKeySpec.getEncoded());
     fos.close();

     PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
             privateKey.getEncoded());
     fos = new FileOutputStream(dir + "/private.key");
     fos.write(pkcs8EncodedKeySpec.getEncoded());
     fos.close();
 }
 //对称
 void saveSymmetricKey(String path, SecretKey secretKey)
         throws IOException {
     X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
             secretKey.getEncoded());
     FileOutputStream keyfos = new FileOutputStream(path);
     keyfos.write(x509EncodedKeySpec.getEncoded());
     keyfos.close();
 }

加密模式

Java SDK CryptoMode Encrypt Decrypt Range Get Multipart Upload
1.7.8.1+ AE AES‑GCM AES‑GCM Yes YES
1.7.8.1+ SAE AES‑GCM AES‑GCM No YES
1.7.8.1+ EO AES‑CBC AES‑GCM AES-CBC YES

CryptoMode

  • AE : AuthenticatedEncryption
  • SAE : StrictAuthenticatedEncryption
  • EO : EncryptionOnly

桶管理

创建桶

您可以通过AmazonS3.createBucket创建一个桶。示例代码如下:

//设置您要创建桶的名称
CreateBucketRequest request = new CreateBucketRequest(bucketName);
//设置桶的权限,如果不设置,默认为Private
request.setCannedAcl(CannedAccessControlList.PublicRead);
AmazonS3.createBucket(request);

注意:

1. 桶的命名规范参见API文档

2. NOS中的桶名是全局唯一的,您或者他人已经创建了同名桶,您无法再创建该名称的桶

列举桶

您可以通过AmazonS3.listBuckets列举出当前用户拥有的所有桶。示例代码如下:

for (Bucket bucket : s3Client.listBuckets()) {
     System.out.println(" - " + bucket.getName());
}

删除桶

您可以通过AmazonS3.deleteBucket删除指定的桶。示例代码如下:

s3Client.deleteBucket(bucketName);

注意:

如果指定的桶不为空(桶中有文件或者未完成的分块上传),则桶无法删除

查看桶是否存在

您可以通过AmazonS3.doesBucketExist查看指定的桶是否存在。示例代码如下:

boolean exists = s3Client.doesBucketExist(bucketName);

注意:

您或者他人已经创建了指定名称的桶,doesBucketExist都会返回true。否则返回false

设置桶的ACL

桶的ACL包含两类:Private(私有), PublicRead(公共读私有写)。您可以通过AmazonS3.setBucketAcl设置桶的权限。

权限 SDK中的对应值
私有读写,对应的Permission为WRITE CannedAccessControlList.Private
公共读私有写,对应的Permission为READ CannedAccessControlList.PublicRead
公共读写 CannedAccessControlList.PublicReadWrite,NOS不支持

示例代码如下:

//method 1,使用header的方式
AmazonS3.setBucketAcl(bucketName, CannedAccessControlList.Private);

//method 2,使用body的方式
AccessControlList accessControlList1 = new AccessControlList();
Grantee grantee = new CanonicalGrantee("aa");
accessControlList1.setOwner(new Owner());
accessControlList1.grantPermission(grantee,Permission.Write);//授权列表中的第一个值为有效值,其他的值会被忽略
s3Client.setBucketAcl(bucketName,accessControlList1);

查看桶的ACL

您可以通过AmazonS3.getBucketAcl()查看桶的权限。示例代码如下:

AccessControlList accessControlList = s3Client.getBucketAcl(bucketName);
System.out.println("owner : " +  accessControlList.getOwner().getId() + " : " + accessControlList.getOwner().getDisplayName());
for(Grant grant : accessControlList.getGrantsAsList()){//NOS由于权限不能赋值给其他的用户,所以返回值中只有一条记录
      System.out.println(grant.getGrantee().getIdentifier() + " : " +  grant.getPermission() + " : " + grant.getGrantee().getTypeIdentifier());
}

文件上传

在NOS中,用户的每个文件都是一个Object(对象)。

NOS提供两种文件上传方式:普通上传(PutObject),上传小于或等于100M的文件;分块上传(MultiUpload),大文件可以采用该方式上传。

AWS Java SDK提供了丰富的文件上传接口与功能,主要有:

  • 直接内容上传
  • 本地文件普通上传
  • 支持上传文件时设置文件元数据信息
  • 流式上传
  • 分块上传

下述除了分块上传不能直接使用加密client替换外,其他的都是可以直接替换使用

直接内容上传

对于一些简单的字符串内容,可以使用putObject进行上传。代码示例如下:

//要上传文件的路径
String content = "Object content";
try {
   s3Client.putObject("your-bucketname","your-objectname",content);
}catch (Exception e){
   System.out.println(e.getMessage());
}

本地文件普通上传

对于小对象可以使用putObject接口进行上传,putObject上传支持的最大文件大小为100M,如果上传大于100M的文件需要使用分块上传。本地文件普通上传的示例代码如下:

//要上传文件的路径
String filePath = "your-local-file-path";
try {
   s3Client.putObject("your-bucketname","your-objectname", new File(filePath));
}catch (Exception e){
   System.out.println(e.getMessage());
}

上传文件时设置文件元数据信息

您可以在上传文件时设置文件元数据信息。可以设置的元数据主要有文件的Content-Type和用户自定义元数据信息。 这里以普通上传为例:

String filePath = "your-local-file-path";
ObjectMetadata objectMetadata = new ObjectMetadata();
//设置Content-Type
objectMetadata.setContentType("application/xml");
//设置标准http消息头(元数据)
objectMetadata.setHeader("Cache-Control", "no-cache");
//设置用户自定义元数据信息
Map<String, String> userMeta = new HashMap<String, String>();
userMeta.put("ud", "test");
objectMetadata.setUserMetadata(userMeta);
PutObjectRequest putObjectRequest = new PutObjectRequest("your-bucketname","your-objectname", new File(filePath));
putObjectRequest.setMetadata(objectMetadata);
s3Client.putObject(putObjectRequest);

流式上传

try {
   ObjectMetadata objectMetadata = new ObjectMetadata();
   //设置流的长度,您还可以设置其他文件元数据信息
   objectMetadata.setContentLength(streamLength);
   s3Client.putObject("your-bucketname","your-objectname", inputStream, objectMetadata)
}catch (Exception e){
   System.out.println(e.getMessage());
}

分块上传

对于大于100M的对象必须进行分块上传,分块上传的最小单位单位为16K,最后一块可以小于16K,最大单位为100M,较大文件使用分块上传失败的代价比较小,只需要重新上传失败的分块即可; 在文件已经全部存在情况下可以进行分块并发上传。

  • 初始化分块上传
//初始化一个分块上传,获取分块上传ID,桶名 + 对像名 + 分块上传ID 唯一确定一个分块上传
FileInputStream is = new FileInputStream("youFilePath");
InitiateMultipartUploadRequest  initRequest = new InitiateMultipartUploadRequest("your-bucketname", "your-objectname");
//您还可以在初始化分块上传时,设置文件的Content-Type
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType("application/xml");
initRequest.setObjectMetadata(objectMetadata);
InitiateMultipartUploadResult initResult = s3Client.initiateMultipartUpload(initRequest);
String uploadId = initResult.getUploadId();
  • 进行分块上传

下面是顺序上传所有分块的示例,您也可以进行并发上传。

long filePosition = 0;
for (int i = 1; filePosition < contentLength; i++) {
    // Last part can be less than 5 MB. Adjust part size.
    partSize = Math.min(partSize, (contentLength - filePosition));
    // Create request to upload a part.
    UploadPartRequest uploadRequest = new UploadPartRequest()
            .withBucketName(bucketName).withKey(key)
            .withUploadId(uploadId).withPartNumber(i)
            .withFileOffset(filePosition)
            .withFile(file) //要上传的文件对象
            .withPartSize(partSize);
    //如果是加密的,需要加入这步
    if (filePosition + partSize == contentLength){
        uploadRequest.setLastPart(true);
    }
    // Upload part and add response to our list.
    partETags.add(encryptionClient.uploadPart(uploadRequest).getPartETag());

    ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, key, initResponse.getUploadId());
    encryptionClient.listParts(listPartsRequest);

    filePosition += partSize;
}
  • 列出所有分块
//这里可以检查分块是否全部上传,分块MD5是否与本地计算相符,如果不符或者缺少可以重新上传

List<PartETag> partETags = new ArrayList<PartETag>();

int nextMarker = 0;
while (true) {
   ListPartsRequest listPartsRequest = new ListPartsRequest("your-bucketname",
                                                 "your-objectname", uploadId);
   listPartsRequest.setPartNumberMarker(nextMarker);

   PartListing partList = s3Client.listParts(listPartsRequest);

   for (PartSummary ps : partList.getParts()) {
       nextMarker++;
       partETags.add(new PartETag(ps.getPartNumber(), ps.getETag()));
   }

   if (!partList.isTruncated()) {
       break;
   }
}
  • 完成分块上传
CompleteMultipartUploadRequest completeRequest =  new CompleteMultipartUploadRequest(
                          "your-bucketname","your-objectname", uploadId, partETags);
CompleteMultipartUploadResult completeResult = s3Client.completeMultipartUpload(completeRequest);
  • 取消分块上传
s3Client.abortMultipartUpload(new AbortMultipartUploadRequest(
                bucketName, key, initResponse.getUploadId()));

文件下载

您可以通过指定桶名和对象名调用getObject接口进行文件下载。AWS S3 Java SDK提供了如下下载方式:

  • 流式下载
  • 下载到本地文件
  • Range下载
  • 指定If-Modified-Since进行下载

也是可以直接使用加密client进行替换,除了StrictAuthenticatedEncryption不支持range

流式下载

S3Object nosObject = AmazonS3.getObject("your-bucketname","your-objectname");
//可以通过NOSObject对象的getObjectMetadata方法获取对象的ContentType等元数据信息
String contentType = nosObject.getObjectMetadata().getContentType();
//流式获取文件内容
InputStream in = nosObject.getObjectContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while (true) {
  String line;
  try {
    line = reader.readLine();
    if (line == null) break;
    System.out.println("\n" + line);
  } catch (IOException e) {
    e.printStackTrace();
  }
}
try {
  reader.close();
} catch (IOException e) {
  e.printStackTrace();
}

注意:

  • AmazonS3.getObject获取的流一定要显式的close,否则会造成资源泄露。

下载到本地文件

你可以通过调用AWS S3 Java SDK的getObject接口直接将NOS中的对象下载到本地文件

String destinationFile = "your-local-filepath";
GetObjectRequest getObjectRequest = new GetObjectRequest("your-bucketname","your-objectname");
ObjectMetadata objectMetadata = s3Client.getObject(getObjectRequest, new File(destinationFile));

Range下载

AWS S3 Java SDK支持范围(Range)下载,即下载指定对象的指定范围的数据。

GetObjectRequest getObjectRequest = new GetObjectRequest("your-bucketname","your-objectname");
getObjectRequest.setRange(0, 100);
S3Object nosObject = s3Client.getObject(getObjectRequest);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while (true) {
  String line;
  try {
    line = reader.readLine();
    if (line == null) break;
      System.out.println("\n" + line);
  } catch (IOException e) {
    e.printStackTrace();
  }
}
try {
  reader.close();
} catch (IOException e) {
  e.printStackTrace();
}

指定If-Modified-Since进行下载

下载文件时,您可以指定If-Modified-Since参数,满足文件的最后修改时间小于等于If-Modified-Since参数指定的时间,则不进行下载,否则正常下载文件。

//假设需要下载的文件的最后修改时间为: Date lastModified;
//lastModified小于等于指定If-Modified-Since参数
GetObjectRequest getObjectRequest = new GetObjectRequest("your-bucketname","your-objectname");
Date afterTime = new Date(lastModified.getTime() + 1000);
getObjectRequest.setModifiedSinceConstraint(afterTime);
//此时nosObject等于null
S3Object s3Object = client.getObject(getObjectRequest);
Date beforeTime = new Date(lastModified.getTime() -1000);
getObjectRequest.setModifiedSinceConstraint(beforeTime);
//此时s3Object不等于null,可以正常获取文件内容
s3Object = client.getObject(getObjectRequest);
BufferedReader reader = new BufferedReader(new InputStreamReader(s3Object.getObjectContent()));
while (true) {
  String line;
  try {
    line = reader.readLine();
    if (line == null) break;
      System.out.println("\n" + line);
  } catch (IOException e) {
    e.printStackTrace();
  }
}
try {
  reader.close();
} catch (IOException e) {
  e.printStackTrace();
}

文件管理

您可以通过AWS S3 Java SDK进行如下文件管理操作:

  • 判断文件是否存在
  • 文件删除
  • 获取文件元数据信息
  • 文件复制(copy)
  • 列举桶内文件
  • 生成私有对象可下载的URL链接

判断文件是否存在

您可以通过AmazonS3.doesObjectExist判断文件是否存在。

boolean isExist = s3Client.doesObjectExist("your-bucketname","your-objectname");

文件删除

您可以通过AmazonS3.deleteObject删除单个文件

s3Client.deleteObject("your-bucketname","your-objectname");

您还可以通过AmazonS3.deleteObjects一次删除多个文件

try {
    DeleteObjectsResult result = s3Client.deleteObjects(deleteObjectsRequest);
    List<DeletedObject>  deleteObjects = result.getDeletedObjects();
    //print the delete results
    for (DeletedObject items: deleteObjects){
      System.out.println(items.getKey());
    }
// 部分对象删除失败
} catch (MultiObjectDeleteException e) {
    List<DeleteError> deleteErrors = e.getErrors();
    for (DeleteError error : deleteErrors) {
        System.out.println(error.getKey());
    }
} catch (AmazonServiceException  e) {
        //捕捉nos服务器异常错误
} catch (AmazonClientException ace) {
       //捕捉客户端错误
}

获取文件元数据信息

您可以通过AmazonS3.getObjectMetadata获取文件元数据信息

s3Client.getObjectMetadata("your-bucketname","your-objectname");

文件复制(copy)

您可以通过AmazonS3.copyObject接口实现文件拷贝功能。 NOS支持桶内copy以及相同用户的跨桶copy。

s3Client.copyObject("source-bucket", "source-object", "dst-bucket", "dst-object");

列举桶内文件

您可以通过AmazonS3.listObjects列出桶里的文件。listObjects接口如果调用成功,会返回一个ObjectListing对象,列举的结果保持在该对象中。

ObjectListing的具体信息如下表所示:

方法 含义
List<S3ObjectSummary> getObjectSummaries() 返回的文件列表(包含文件的名称、Etag的元数据信息)
String getPrefix() 本次查询的文件名前缀
String getDelimiter() 文件分界符
String getMarker() 这次List Objects的起点
int getMaxKeys() 响应请求内返回结果的最大数目
String getNextMarker() 下一次List Object的起点
boolean isTruncated() 是否截断,如果因为设置了limit导致不是所有的数据集都返回,则该值设置为true
List<String> getCommonPrefixes() 如果请求中指定了delimiter参数,则返回的包含CommonPrefixes元素。该元素标明以delimiter结尾,并有共同前缀的对象的集合

AmazonS3.listObjects接口提供两种调用方式:简单列举、通过ListObjectsRequest列举

简单列举

简单列举只需指定需要列举的桶名,最多返回100条对象记录,建议桶内对象数较少时(小于100)使用。

ObjectListing objectListing = AmazonS3.listObjects("your-bucketname");
List<S3ObjectSummary> sums = objectListing.getObjectSummaries();
for (S3ObjectSummary s : sums) {
  System.out.println("\t" + s.getKey());
}

通过ListObjectsRequest列举

您还可以通过设置ListObjectsReques参数实现各种灵活的查询功能。ListObjectsReques的可设置的参数如下:

设置方法 作用
setPrefix(String prefix) 限定返回的object key必须以prefix作为前缀
setDelimiter(String delimiter) 是一个用于对Object名字进行分组的字符。所有名字包含指定的前缀且第一次出现delimiter字符之间的object作为一组元素——CommonPrefixes
setMarker(String marker) 字典序的起始标记,只列出该标记之后的部分
setMaxKeys(Integer maxKeys) 限定返回的数量,返回的结果小于或等于该值(默认值为100)
1、分页列举桶内的所有文件:
List<S3ObjectSummary> listResult = new ArrayList<S3ObjectSummary>();
ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
listObjectsRequest.setBucketName("your-bucketname");
listObjectsRequest.setMaxKeys(50);
ObjectListing listObjects = AmazonS3.listObjects(listObjectsRequest);
do {
  listResult.addAll(listObjects.getObjectSummaries());
  if (listObjects.isTruncated()) {
    ListObjectsRequest request = new ListObjectsRequest();
    request.setBucketName(listObjectsRequest.getBucketName());
    request.setMarker(listObjects.getNextMarker());
    listObjects =  AmazonS3.listObjects(request);
  } else {
    break;
  }
} while (listObjects != null);
2、使用Delimiter模拟文件夹功能

假设桶内有如下对象:a/1.jpg、a/2.jpg、a/b/1.txt、a/b/2.txt,列举a文件夹下的文件及子文件夹的示例代码如下:

ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
listObjectsRequest.setBucketName("your-bucketname");
listObjectsRequest.setDelimiter("/");
listObjectsRequest.setPrefix("a/");
ObjectListing listing = s3Client.listObjects(listObjectsRequest);
// 遍历所有Object
System.out.println("Objects:");
for (S3ObjectSummary objectSummary : listing.getObjectSummaries()) {
    System.out.println(objectSummary.getKey());
}
// 遍历所有CommonPrefix
System.out.println("CommonPrefixs:");
for (String commonPrefix : listing.getCommonPrefixes()) {
    System.out.println(commonPrefix);
}

示例代码的输出如下:

Objects:

a/1.jpg

a/2.jpg

CommonPrefixs:

a/b/

生成私有对象可下载的URL链接

AWS Java SDK支持生成可下载私有对象的URL连接,您可以将该链接提供给第三方进行文件下载:

GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, key);
// 设置可下载URL的过期时间为1天后
generatePresignedUrlRequest.setExpiration(new Date(System.currentTimeMillis()+3600*1000*24));
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);//生成URL
System.out.println(url);//可以用这个url来下载文件

文件上传下载工具类:TransferManager

前文提到的是NOS Java SDK提供的基础接口,为方便用户进行文件上传下载,NOS Java SDK提供了封装更好、使用更方便的工具类:TransferManager。

TransferManager的初始化

//先实例化一个AmazonS3
String accessKey = "your-accesskey";
String secretKey = "your-secretKey ";
Credentials credentials = new BasicCredentials(accessKey, secretKey);
AmazonS3 AmazonS3 = new AmazonS3(credentials);
AmazonS3.setEndpoint(endPoint);
//然后通过AmazonS3对象来初始化TransferManager
TransferManager transferManager = new TransferManager(AmazonS3);
Download download = transferManager.download(TestConfig.bucketName,key,new File("localFilePath"));
try {
    download.waitForCompletion();
} catch (InterruptedException e) {
    e.printStackTrace();
}

使用TransferManager进行文件上传

TransferManager会根据文件大小,选择是否进行分块上传。当文件小于等于16M时,TransferManager会自动调用PutObject接口,否则TransferManager会自动对文件进行分块上传。

1、上传本地文件:

如果指定上传的本地文件大于16M,TransferManager会自动对文件进行分块,并发调用分块上传接口进行上传,大大提高上传文件的速度。

//上传文件
Upload upload = transferManager.upload("your-bucketname", "your-objectname", new File("your-file"));
try {
    upload.waitForUploadResult();
} catch (InterruptedException e) {
    e.printStackTrace();
}

2、流式上传:

您也可以使用TransferManager进行流式上传,但是相比本地文件上传,流式上传无法做到多个分块并发上传,只能一个分块一个分块顺序上传。

//流式上传文件
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.length());
Upload upload = transferManager.upload("your-bucketname", "your-objectname", inputStream, objectMetadata);
UploadResult  result = upload.waitForUploadResult();

3、上传目录

您可以使用TransferManager将某个目录下的文件全部上传到NOS,对象名即文件名

3.1 不支持多级目录

MultipleFileUpload result = transferManager.uploadDirectory("your-buckename", null, new File("dirPath"), false);
result.waitForCompletion();

3.2 支持多级目录,会递归的上传目录下的所有文件

MultipleFileUpload result = transferManager.uploadDirectory("your-buckename", null, new File("dirPath"), true);
result.waitForCompletion();

4、下载文件

File file = new  File("your-destFile");
Download download = transferManager.download("your-bucketname", "your-objectname", file);
download.waitForCompletion();