S3是亚马逊2006年推出的简单存储服务(Simple Storage Service),理论上是一个全球存储区域网络,你可以把它想像成一个超大的硬盘,可以在其中存储和检索数字资产,通过 S3 存储和检索的资产被称为对象,对象存储在存储段(bucket)中。
很多公司都推出了自己的对象存储服务,例如阿里云对象存储服务OSS,可以使用S3 API进行访问。
话不多说,我们看看如何读写S3。
➤ 通过第三方工具
这种工具很多,s3-command-line 就是一个。这是一个GoLang开发的命令行工具,编译出来只有13MB。它支持文件的上传、下载、删除、列表,已经满足了大多数使用场景需求。
checkout它的源码,在安装有Go语言环境的服务器上执行 go build 即可编译出可执行程序 s3-command-line,这个只需要做一次,最终运行 s3-command-line 软件的服务器上并不需要安装GoLang环境。
在使用 s3-command-line 之前,我们需要创建一个配置文件 config.json,填入我们访问S3所需的密钥等信息:
{
"id": "...",
"secret": "...",
"endPoint": "http://...",
"region": "..."
}
文章来源:https://www.codelast.com/
这些信息你可以在你使用的S3平台上找到。
假设我们要读写的bucket名为 my-bucket,那么下面的命令会告诉你怎么使用 s3-command-line:
列出指定 bucket 里的文件:
./s3-command-line ls -c config.json my-bucket /
从指定的 bucket 删除文件 1.txt:
./s3-command-line rm -c config.json my-bucket 1.txt
上传本地文件 1.txt 到指定的 bucket:
./s3-command-line put -c config.json my-bucket 1.txt
从指定的 bucket 下载文件 1.txt 到本地:
./s3-command-line get -c config.json my-bucket 1.txt
文章来源:https://www.codelast.com/
总结:简单好用,编译成可执行程序后没有对其他library的依赖,令人舒适。
➤ 自己写JAVA程序
JDK版本:1.8.0_221,Maven:3.6.3
首先你需要在 pom.xml 中添加相关依赖(参考这里):
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-sdk-java</artifactId>
<version>2.18.16</version>
</dependency>
文章来源:https://www.codelast.com/
然后就可以开始写代码了(测试可用):
package com.codelast;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Exception;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
/**
* S3存储操作类。
* 参考:https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PutObject.java
*
* @author DarranZhang@codelast.com
* @version 2022-11-14
*/
@Slf4j
public class S3Operator {
public static final String ACCESS_KEY = "your_key";
public static final String SECRET_KEY = "your_secret";
public static final String END_POINT = "http://your_end_point_url";
public static final String REGION = "your_region";
public static final String BUCKET_NAME = "your_bucket_name";
private static class Options {
@Option(name = "--localFile", usage = "the path of the local file to upload to S3", required = true)
String localFile;
}
public static void main(String[] args) throws Exception {
Options opts = new Options();
CmdLineParser parser = new CmdLineParser(opts);
try {
parser.parseArgument(args);
} catch (CmdLineException e) {
log.error("command parse fail", e);
System.exit(1);
}
S3Operator operator = new S3Operator();
operator.upload(opts.localFile);
}
private void upload(String localFile) throws Exception {
AwsBasicCredentials credentials = AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY);
Region region = Region.of(REGION);
S3Client s3Client = S3Client.builder()
.endpointOverride(new URI(END_POINT))
.region(region)
.credentialsProvider(StaticCredentialsProvider.create(credentials))
.build();
String s3FileName = StringUtils.substringAfterLast(localFile, "/"); // 上传到S3之后的文件名,不一定要和本地文件名一致,但为了直观,这里保持一致
if (StringUtils.isEmpty(s3FileName)) {
String msg = "local file name is empty";
log.error(msg);
throw new RuntimeException(msg);
}
// 每次上传前,不用先删除再上传,因为最新上传的同名文件会覆盖之前的文件
putObject(s3Client, BUCKET_NAME, s3FileName, localFile);
s3Client.close();
}
public static void putObject(S3Client s3Client, String bucketName, String objectKey, String objectPath) {
try {
PutObjectRequest request = PutObjectRequest.builder().bucket(bucketName).key(objectKey).build();
PutObjectResponse response = s3Client.putObject(request, RequestBody.fromBytes(getObjectFile(objectPath)));
log.info("successfully upload file [{}] to S3, entity tag: [{}]", objectPath, response.eTag());
} catch (S3Exception e) {
log.error(String.format("failed to upload file [%s] to S3", objectPath), e);
}
}
private static byte[] getObjectFile(String filePath) {
FileInputStream fileInputStream = null;
byte[] bytesArray = null;
try {
File file = new File(filePath);
bytesArray = new byte[(int) file.length()];
fileInputStream = new FileInputStream(file);
fileInputStream.read(bytesArray);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bytesArray;
}
private static void deleteObject(S3Client s3, String bucket, String s3FileName) {
DeleteObjectRequest request = DeleteObjectRequest.builder().bucket(bucket).key(s3FileName).build();
s3.deleteObject(request);
}
}
这样我们就实现了用JAVA访问S3,不过,如果你仔细看会发现,当引入S3的依赖后,你的项目编译出的 fat jar 体积剧增了400MB左右!这也太恶心了...
当然,上面引入的 whole-SDK 依赖,是我为了省事引入了所有S3相关的模块,或许你有些用不到的模块,可以不用引入 whole-SDK,而是分别引入单独的模块,但我有空去想这些玩意的话,还不如使用 s3-command-line 简单呢,不是吗?毕竟时间就是生命,别在这些无聊的事上浪费生命。
文章来源:https://www.codelast.com/
➤ 总结
如果你操作S3时只使用上传、下载、删除、列表功能,那么建议不要自己写代码,使用 s3-command-line 更香。
文章来源:https://www.codelast.com/
➤➤ 版权声明 ➤➤
转载需注明出处:codelast.com
感谢关注我的微信公众号(微信扫一扫):
以及我的微信视频号: