系列文章目录
第一章 上传云对象存储代码实践-华为云OBS
第二章 上传云对象存储代码实践-阿里云OSS
阿里云OSS
一、阿里云OSS专用名词解释
存储空间(Bucket)
存储空间是OSS中用于存储对象的容器,类似于文件系统中的文件夹,但具有全局唯一性。每个存储空间可设置独立的权限策略、生命周期规则和日志记录。
对象(Object)
对象是OSS存储的基本单元,可以是任意类型的数据(如文本、图片、视频等)。每个对象由键(Key)、数据(Data)和元信息(Metadata)组成,可通过唯一的URL访问。
地域(Region)
地域指OSS数据中心所在的物理位置。用户可选择就近地域创建存储空间,以减少延迟并提高访问速度。不同地域的OSS资源默认隔离。
访问域名(Endpoint)
Endpoint是OSS服务的访问地址,格式为<BucketName>.<Region>.aliyuncs.com。不同地域的Endpoint不同,内网和外网访问需使用对应的域名。
访问密钥(AccessKey)
AccessKey由AccessKey ID和AccessKey Secret组成,用于身份验证和API请求签名。分为主账号AccessKey和子账号RAM用户的AccessKey。
权限控制(ACL)
ACL(Access Control List)用于定义Bucket或Object的访问权限,包括私有(Private)、公共读(Public Read)和公共读写(Public Read/Write)。
生命周期规则(Lifecycle)
生命周期规则可自动管理对象的存储周期,例如自动删除过期文件或转换存储类型(如从标准存储转为低频访问存储)。
跨域资源共享(CORS)
CORS规则允许浏览器跨域访问OSS资源,需在Bucket中配置允许的源(Origin)、方法(Method)和头部(Headers)。
防盗链(Referer)
通过设置Referer白名单或黑名单,限制仅特定来源的请求可访问Bucket资源,防止盗链行为。
数据加密
支持服务器端加密(SSE)和客户端加密。SSE包括OSS完全托管加密(SSE-OSS)和KMS托管加密(SSE-KMS)。
日志记录(Logging)
可开启Bucket访问日志功能,将请求记录保存到指定的Bucket中,便于审计和分析。
图片处理(Image Service)
通过URL参数对存储在OSS中的图片进行裁剪、缩放、水印等实时处理,无需下载原图。
版本控制(Versioning)
启用版本控制后,覆盖或删除Object时会保留旧版本,支持恢复误删或覆盖的文件。
跨区域复制(Cross-Region Replication)
自动将源Bucket中的Object复制到目标Bucket,适用于数据容灾或合规性需求。
传输加速(Transfer Acceleration)
利用全球分布的边缘节点加速文件上传下载,适用于远距离或弱网络环境的数据传输。
冷归档存储(Cold Archive)
适用于极低频访问数据的存储类型,成本最低但 retrieval 时间较长(需解冻)。
碎片(Multipart Upload)
大文件上传时可分片传输,提高稳定性。每个分片需单独上传,最终合并为完整对象。
二、使用步骤和代码实践
OSS对象存储界面创建AK SK


- 妥善保存ak sk
创建存储桶,记住桶名,地域节点。


上传代码JAVA-分片上传

分片上传(Multipart Upload)分为以下三个步骤:
-
调用InitiateMultipartUpload接口初始化分片上传任务。
-
调用UploadPart接口上传分片。
-
要上传的文件切分成Part之后,文件顺序是通过上传过程中指定的partNumber来确定的,实际执行中并没有顺序要求,因此可以实现并发上传。并发数并非越多越快,请结合自身网络状况和设备负载综合考虑。
默认情况下,已经上传但还没有调用CompleteMultipartUpload的Part不会被自动删除,因此如果要终止上传并删除占用的存储空间,需要调用AbortMultipartUpload。
调用CompleteMultipartUpload接口将Part合并为Object。
使用OSS Java SDK完成分片上传的示例代码如下,替换示例中的ak sk、endpoint 、bucketName 、region。
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.Mimetypes;
import com.aliyun.oss.model.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
String ak = "xxx";
String sk = "xxx";
StaticCredentialProvider credentialsProvider = StaticCredentialProvider.create(Credential.builder()
.accessKeyId(ak)
.accessKeySecret(sk)
.build());
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "exampledir/exampleobject.txt";
// 待上传本地文件路径。
String filePath = "D:\\localpath\\examplefile.txt";
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
String region = "cn-hangzhou";
// 创建OSSClient实例。
// 当OSSClient实例不再使用时,调用shutdown方法以释放资源。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 创建InitiateMultipartUploadRequest对象。
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);
// 创建ObjectMetadata并设置Content-Type。
ObjectMetadata metadata = new ObjectMetadata();
if (metadata.getContentType() == null) {
metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(filePath), objectName));
}
System.out.println("Content-Type: " + metadata.getContentType());
// 将metadata绑定到上传请求中。
request.setObjectMetadata(metadata);
// 初始化分片。
InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
// 返回uploadId。
String uploadId = upresult.getUploadId();
// partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
List<PartETag> partETags = new ArrayList<PartETag>();
// 每个分片的大小,用于计算文件有多少个分片。单位为字节。
// 分片最小值为100 KB,最大值为5 GB。最后一个分片的大小允许小于100 KB。
// 设置分片大小为 1 MB。
final long partSize = 1 * 1024 * 1024L;
// 根据上传的数据大小计算分片数。以本地文件为例,说明如何通过File.length()获取上传数据的大小。
final File sampleFile = new File(filePath);
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;
UploadPartRequest uploadPartRequest = new UploadPartRequest();
uploadPartRequest.setBucketName(bucketName);
uploadPartRequest.setKey(objectName);
uploadPartRequest.setUploadId(uploadId);
// 设置上传的分片流。
// 以本地文件为例说明如何创建FileInputStream,并通过InputStream.skip()方法跳过指定数据。
InputStream instream = new FileInputStream(sampleFile);
instream.skip(startPos);
uploadPartRequest.setInputStream(instream);
// 设置分片大小。
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());
// 关闭流
instream.close();
}
// 创建CompleteMultipartUploadRequest对象。
// 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
CompleteMultipartUploadRequest completeMultipartUploadRequest =
new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
// 完成分片上传。
CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
System.out.println("上传成功,ETag:" + completeMultipartUploadResult.getETag());
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught a ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
使用预签名URL下载或预览文件
OSS文件默认为私有权限,只有文件拥有者可访问。但文件拥有者可以生成分享链接(预签名URL),授权他人在有效期内下载或在线预览指定文件。

工作原理
预签名URL的生成过程依赖密钥加密和参数拼接,过程如下:
-
权限校验:您生成预签名URL时需拥有oss:GetObject权限,第三方才能通过该预签名URL成功下载/预览文件;
-
本地加密:基于AK/SK对文件路径、过期时间等信息加密计算,得到签名(x-oss-signature);
-
附加签名:将签名参数(x-oss-date、x-oss-expires、x-oss-credential 等)作为查询字符串附加到文件URL;
-
形成链接:组成完整的预签名URL。
预签名URL格式
https://BucketName.Endpoint/Object?签名参数
完整示例
https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject.txt?x-oss-process=image%2Fresize%2Cp_10&x-oss-date=20241115T095058Z&x-oss-expires=3600&x-oss-sign
以下是java代码的生成文件的下载链接(预签名URL)的代码示例。
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import java.net.URL;
import java.util.Date;
public class Demo {
public static void main(String[] args) throws Throwable {
// 以华东1(杭州)的外网Endpoint为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
String ak = "xxx";
String sk = "xxx";
StaticCredentialProvider credentialsProvider = StaticCredentialProvider.create(Credential.builder()
.accessKeyId(ak)
.accessKeySecret(sk)
.build());
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,例如exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "exampleobject.txt";
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
String region = "cn-hangzhou";
// 创建OSSClient实例。
// 当OSSClient实例不再使用时,调用shutdown方法以释放资源。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 设置预签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
// 生成以GET方法访问的预签名URL。本示例没有额外请求头,其他人可以直接通过浏览器访问相关内容。
URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
System.out.println(url);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
2741

被折叠的 条评论
为什么被折叠?



