OSS( Object Storage Service)
Java制作项目的过程中想实现文件上传功能,使用传统的上传文件存储到本地磁盘会存在许多弊端,并且极其不方便等问题 。使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频 等在内的各种非结构化数据文件。
1、创建一个或者多个存储空间,向每个存储空间中添加一个或多个文件。
2、通过获取已上传文件的地址进行文件的分享和下载。
3、通过修改存储空间或文件的属性或元信息来设置相应的访问权限。
4、在阿里云管理控制台执行基本和高级OSS任务。
5、使用阿里云开发工具包或直接在应用程序中进行RESTful API调用执行基本和高级 OSS任务
1.注册阿里云账号
2.实名认证:
鼠标移动到头像,点击实名认证
3.开通OSS服务:
移动到三条杠这里
搜索OSS对象或者点击对象存储
点击开通服务
去支付0元
4.创建bucket
Bucket 就是你用来存储数据的地方,所有的对象(文件)都必须存放在某个 Bucket 中。每个 Bucket 可以包含多个对象(文件)
点击概况,点击创建bucket
输入名称,必须是小写
地区选离的近的: 选择接近用户或应用的地域可以降低访问延迟。如果你的应用主要在某个地区(例如中国、美国或欧洲)运行,最好选择离用户更近的区域,这样数据传输速度会更快,减少访问延迟。
注意一下这里,这里创建的时候不能修改,等到创建完成后可以把私有改成公共的
完成了创建
点击这里就可以看到我们创建的bucket
5、获取AccessKey ID 和 AccessKey Secret
在控制台中---》头像---》AccessKey
创建
通过验证
注意:!!!!创建好key后会弹出来
一定要保存好,没保存好就再也找不到
后面要用到
后续修改设置:完成所有的再设置这个
还记得当时我们创建的时候初始化是私有的吗,现在设置成公告访问的
将 Bucket 设置为公告访问使存储桶中的文件对外公开,任何人都可以通过 URL 访问这些文件。
2.OSS快速入门:
问题:SDK是什么??
SDK 是开发某个特定平台、应用或服务所需的一组工具和资源,通常包括库、API、工具、文档等。 (也就是官方提供的帮助开发的)
1.从官网获取相关SDK依赖:
点击
点击SDK示例
使用1.8版本引入此代码即可!无需再次引入下面的代码!
//创建测试工程,引入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>
如果使用的是Java 9及以上的版本,需要添加的代码如下:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
2.配置环境变量:
到时候代码从环境变量中拿到这俩变量
新建环境变量
变量名:OSS_ACCESS_KEY_ID
变量名:AccessKey ID
3.官方代码
这里都是官方给的代码示例
查看OSS对应域名
因为当时我们创建bucket的时候地域不同,在这里找到对应的地域
我当时选的是北京,找到对应的就行 oss-cn-beijing
3.代码:
1.上传:
1.简单上传图片:
第三行:修改为自己的地域对应的
第七行:bucketName设置为Bucket名称
第九行:objectName设置为再Bucket中路径
第十二行:filePath设置本地要上传的路径+文件名
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-beijing.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "springboottest01";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String objectName = "springboottest01/1.png";
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
String filePath= "D:\\test\\1.png";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
try {
InputStream inputStream = new FileInputStream(filePath);
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);
// 创建PutObject请求。
PutObjectResult result = ossClient.putObject(putObjectRequest);
} 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();
}
}
}
上传后打开自己创建的bucket就可以看到上传的1.png了
2.下载:
1.断点续传下载:
在下载OSS大文件(超过5 GB)到本地的过程中,如果出现网络中断、程序异常退出等问题导致文件下载失败,甚至重试多次仍无法完成下载,您需要使用断点续传下载的方式。断点续传下载将需要下载的大文件分成多个较小的分片并发下载,加速下载完成时间。如果下载过程中,某一分片下载失败,再次下载时会从Checkpoint文件记录的断点继续下载,无需重新下载所有分片。下载完成后,所有分片将合并成完整的文件。
public class demo {
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-beijing.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "springboottest01";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "springboottest01/1.png";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
try {
// 请求10个任务并发下载。
DownloadFileRequest downloadFileRequest = new DownloadFileRequest(bucketName, objectName);
// 指定Object下载到本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
downloadFileRequest.setDownloadFile("D:\\local\\1.png");
// 设置分片大小,单位为字节,取值范围为100 KB~5 GB。默认值为100 KB。
downloadFileRequest.setPartSize(1 * 1024 * 1024);
// 设置分片下载的并发数,默认值为1。
downloadFileRequest.setTaskNum(10);
// 开启断点续传下载,默认关闭。
downloadFileRequest.setEnableCheckpoint(true);
// 设置断点记录文件的完整路径,例如D:\\localpath\\examplefile.txt.dcp。
// 只有当Object下载中断产生了断点记录文件后,如果需要继续下载该Object,才需要设置对应的断点记录文件。下载完成后,该文件会被删除。
//downloadFileRequest.setCheckpointFile("D:\\localpath\\examplefile.txt.dcp");
// 下载文件。
DownloadFileResult downloadRes = ossClient.downloadFile(downloadFileRequest);
// 下载成功时,会返回文件元数据。
ObjectMetadata objectMetadata = downloadRes.getObjectMetadata();
System.out.println(objectMetadata.getETag());
System.out.println(objectMetadata.getLastModified());
System.out.println(objectMetadata.getUserMetadata().get("meta"));
} 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 (Throwable 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();
}
}
}
}
2.流式下载:
数据并不是一次性地下载到客户端,而是分批或分段地传输。在接收数据的同时,客户端可以开始处理已经接收到的数据。
这种方法适合下载超过内存限制的文件、实时处理数据以减少内存占用,以及通过网络分步获取数据的场景
例子: 客户端在接收到数据后可以立刻开始处理(例如观看视频或听音乐),而不需要等待全部数据下载完毕。
OSS逐块读取文件内容,并将其存储到字节数组中。
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class Stream {
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。关于其他Region对应的Endpoint信息,请参见访问域名和数据中心。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "exampledir/exampleobject.txt";
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
String region = "cn-hangzhou";
// 创建OSSClient实例。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// ossObject包含文件所在的存储空间名称、文件名称、文件元数据以及一个输入流。
OSSObject ossObject = ossClient.getObject(bucketName, objectName);
InputStream inputStream = ossObject.getObjectContent();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 读取文件内容到字节数组。
byte[] readBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(readBuffer)) != -1) {
byteArrayOutputStream.write(readBuffer, 0, bytesRead);
}
// 获取最终的字节数组。
byte[] fileBytes = byteArrayOutputStream.toByteArray();
// 打印字节数组的长度。
System.out.println("Downloaded file size: " + fileBytes.length + " bytes");
// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
inputStream.close();
byteArrayOutputStream.close();
// ossObject对象使用完毕后必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
ossObject.close();
} 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 (Throwable 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();
}
}
}
}
3.批量:
官方给的方法:
3.1ossutil:
这里使用ossutil
使用ossutil,您可以在Windows、macOS和Linux系统上通过命令行高效管理阿里云对象存储服务(OSS),并执行批量操作或自动化任务。本文将介绍如何通过命令行完成创建存储空间(Bucket)、上传文件、下载文件、列举文件,以及删除文件和删除Bucket等操作。
安装:
cmd进入ossutil.exe那一层,然后输入下面这句代码配置
ossutil.exe config
默认路径为 "C:\Users\Administrator.ossutilconfig",按回车键将使用默认文件
输入id和密钥,设置地区,千万不要多加一个oss-(我就这样错的)
ossutil批量上传:
ossutil cp -r D:/local/img oss://my-bucket/img/
例子:把本地D:/local/img上传到桶名/img文件夹下
ossutil cp -r D:/local/img oss://springboottest01/img/
ossutil批量下载:
ossutil cp oss://example_bucket/path/to/directory/ ./local_dir/ -r
例子:从桶名/文件下载到本地imags文件夹下
ossutil cp oss://springboottest01/img/ D:/imags -r
这样这三张图片就下载下来了
3.2 OSS Java SDK
阿里云的 OSS Java SDK 本身并不直接提供多文件下载(批量下载)的接口,但是可以通过并发的方式来实现批量下载多个文件。可以创建多个线程来并行下载多个文件
1.批量上传单线程:
@SpringBootTest
class moreTest02 {
public static final String endpoint = "你的endpoint";
public static final String accessKeyId = "你的accessKeyId";
public static final String accessKeySecret = "你的accessKeySecret";
public static final String bucketName = "你的桶名";
public static OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
@Test
void contextLoads() {
}
public static void main(String[] args) throws InterruptedException {
//单线程插入
Long start = System.currentTimeMillis();
String[] urls = new String[2];
urls[0] = "D:/local/img/5.png";
urls[1] = "D:/local/img/4.png";
Thread uploaderThread = null;
for (String url : urls) {
// String fileBaseName = url.substring(url.lastIndexOf("/") + 1);
try {
ossClient.putObject(bucketName, "fileBaseName.png", new File(url));
System.out.println(url + "上传图片完成!");
} catch (Exception e) {
System.out.println(url + "上传图片失败!");
e.printStackTrace();
}
}
Long end = System.currentTimeMillis();
Long time = end - start;
System.out.println("time = " + time);
ossClient.shutdown();
}
}
2.批量上传多线程:
@SpringBootTest
class moreTest01 {
/**
* 多线程上传图片
*/
public static final String endpoint = "你的endpoint";
public static final String accessKeyId = "你的accessKeyId";
public static final String accessKeySecret = "你的accessKeySecret";
public static final String bucketName = "你的桶名";
public static OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
public static void main(String[] args) throws InterruptedException {
//多线程插入
Long start = System.currentTimeMillis();
System.out.println("start = " + start);
String[] urls = new String[2];
urls[0] = "D:/local/img/5.png";
urls[1] = "D:/local/img/4.png";
Thread uploaderThread = null;
for (String url : urls) {
uploaderThread = new Thread(new Fileuploader(url,start));
uploaderThread.start();
}
Thread.sleep(5000);
ossClient.shutdown();
}
// 文件上传器
static class Fileuploader implements Runnable{
private final String fileURL;
private final Long start;
public Fileuploader(String fileURL,Long start) {
this.fileURL = fileURL;
this.start = start;
}
@Override
public void run() {
String fileBaseName = fileURL.substring(fileURL.lastIndexOf("/") + 1);
try {
ossClient.putObject(bucketName, fileBaseName, new File(fileURL));
System.out.println(fileURL + "上传图片完成!");
Long end = System.currentTimeMillis();
Long time = end - start;
System.out.println("time = " + time);
} catch (Exception e) {
System.out.println(fileURL + "上传图片失败!");
e.printStackTrace();
}
}
}
}
3.使用CompletableFuture实现异步编程
我有一个问题:多线程和异步有什么区别???这个交给你们尝试
@SpringBootTest
public class CompleteTableTests {
@Test
void contextLoads() {
}
public static final String endpoint = "你的endpoint";
public static final String accessKeyId = "你的accessKeyId";
public static final String accessKeySecret = "你的accessKeySecret";
public static final String bucketName = "你的桶名";
public static OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
public static void main(String[] args) {
//使用completableFuture多线程
Long start = System.currentTimeMillis();
String[] urls = new String[3];
urls[0] = "E:/IDEABackground/wallhaven-j3dkjq.png";
urls[1] = "E:/IDEABackground/wallhaven-rd6req.png";
urls[2] = "E:/IDEABackground/wallhaven-rdpvmm.png";
//异步无返回值
for (String url : urls) {
System.out.println("url = " + url);
CompletableFuture<Void> f1 = CompletableFuture.runAsync(()->{
System.out.println(start);
try {
System.out.println(start);
ossClient.putObject(bucketName, "fileBaseName.png", new File(url));
System.out.println(url + "上传图片完成!");
} catch (Exception e) {
System.out.println(url + "上传图片失败!");
e.printStackTrace();
}
// ossClient.shutdown();
});
f1.whenComplete((Long, throwable) -> {
Long end = System.currentTimeMillis();
Long time = end - start;
System.out.println("time = " + time);
});
}
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3ossbrowser
ossbrowser是阿里云官方提供的OSS图形化管理工具,
4.拓展知识点:
1.冷归档存储和深度冷归档存储
冷归档、深度冷归档不支持开启直读,冷归档、深度冷归档类型的Object需要解冻后才能读取。
1.1冷归档:
冷归档存储通常指存储那些较少访问、长期存储的数据。例如,备份数据、日志文件、合规性数据等,这些数据的访问频率较低,但必须保存一定时间以满足法律或行业规定。
与常规存储相比,冷归档存储的存储费用通常较低,但读取数据的费用较高。
由于冷归档存储的目的是节省成本,它可能需要一定的时间来检索数据。当需要访问这些数据时,恢复时间通常较慢,需要几小时或几天不等。
1.2深度冷归档:
深度冷归档存储是冷归档存储的更高效、更便宜的一种类型,专门用于存储几乎永远不会访问的数据,或者只在非常长时间后才可能需要访问的数据。例如,用于存档历史数据、长期存储的记录等。
- 价格比冷归档存储更低
- 恢复时间非常长
2:SDK 代表 Software Development Kit(软件开发工具包)
是一组为开发者提供的工具、库和文档,用于帮助他们创建应用程序或软件。SDK 通常包含开发所需的必要资源 API(应用程序接口),库和框架,示例代码等
5.阿里云存储深入了解:
5.1层级问题
一个桶里放一些文件。
对象存储没有目录层级结构
虽然我们可以在这里看到有文件夹
这其实只是模拟实现的。
Object 会存储 id、文件内容、元数据三部分信息:
就像打了个 标签 一样,并不是说文件存储在这个 标签 下,只是你可以用这个 标签 来检索文件。
5.2:accessKey 不够安全
不提供资源拥有者所属账号的访问密钥(AccessKey)的情况下,通过临时访问凭证以及签名URL的方式授权第三方下载文件(Object)。
阿里云STS(Security Token Service)是阿里云提供的一种临时访问权限管理服务。RAM提供RAM用户和RAM角色两种身份。其中,RAM角色不具备永久身份凭证,而只能通过STS获取可以自定义时效和访问权限的临时身份凭证,即安全令牌(STS Token)。
先创建用户再授权:
这个页面记得保存,不然就找不到了
6.上传头像到OSS:
1.工具类:
@ConfigurationProperties(prefix = "oss")注解用于将配置文件中的属性绑定到Java对象的字段上。prefix = "oss"指定了配置文件中属性的前缀,即所有以oss.开头的属性都会被绑定到AliOSSUtils类的相应字段上。
自动从配置文件中读取并绑定相应的属性值
/**
* 阿里云 OSS 工具配置类
*/
@Data
@Component
@ConfigurationProperties(prefix = "oss")
public class AliOSSUtils {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
2.配置文件:
oss:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: 你的id
access-key-secret: 你的密钥
bucket-name: springboottest01
3.controller:
@Controller
@ResponseBody
public class OssUploadController {
@Autowired
private AliOSSUtils aliOSSUtils;
/**
* 阿里云OSS存储:将用户上传的图片放入阿里云OSS上
* @throws IOException
*/
@PostMapping("/ossupload")
public String uploadDemo(String username, MultipartFile image) throws IOException {
// 用户名,作参照,无意义,去掉也行
System.out.println(username);
// 1.获取用户上传的文件名
String filename = image.getOriginalFilename();
// 优化:为了保证图片名称的唯一性 不会被多用户覆盖
filename = UUID.randomUUID() + filename.substring(filename.lastIndexOf("."));
// 创建OSSClient实例
OSS ossClient = new OSSClientBuilder().build(aliOSSUtils.getEndpoint(), aliOSSUtils.getAccessKeyId(), aliOSSUtils.getAccessKeySecret());
try {
// 上传文件到OSS
ossClient.putObject(aliOSSUtils.getBucketName(), filename, image.getInputStream());
System.out.println("File uploaded: " + filename);
} catch (Exception e) {
e.printStackTrace();
return "上传失败";
} finally {
// 关闭OSSClient
ossClient.shutdown();
}
return "上传成功";
}
}
4.html代码:
enctype="multipart/form-data"用于指定表单数据的编码类型。它在文件上传场景中非常重要。
- 只有multipart/form-data编码类型支持文件上传。其他编码类型无法处理文件数据
- multipart/form-data 。 指定传输数据为二进制类型,比如图片、mp3、文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图片上传</title>
</head>
<body>
<form method="post" action="/ossupload" enctype="multipart/form-data">
用户名:<input name="username" type="text" />
头像:<input name="image" type="file"/>
提交:<input type="submit" value="提交">
</form>
</body>
</html>