springboot操作阿⾥云OSS实现⽂件上传,下载,删除功能
⽬录
1.需求
2. 阿⾥云OSS购买和配置
3. Springboot操作OSS
4.运⾏项⽬测试API接⼝
参考资料:
学会看⽂档,并实际运⽤也是⼀种习惯和技能
下⾯就来简单⼊门⼀下,⽤当下⽐较热门的Springboot 去操作阿⾥云OSS⽂件存储。
1.需求
(没踩过下⾯的坑的⼩伙伴可以直接跳过这⼀章节)
问题简述
⾸先,我在之前⾃⼰做⼀些开源⼩项⽬案例中遇到⼀些⽂件上传下载的问题,⽐如在本机⽂件上传和下载都可以正常使⽤,通过将⽂件上传到Springboot项⽬的根⽬录下,按⽇期分⽂件夹,⽂件访问也很⽅便,可以直接返回⽂件相对路径地址,并直接可以访问。
问题
然⽽,这种⽅式存在弊端,因为当项⽬打包(jar包)部署阿⾥云学⽣机后,出现类似(No Such Directory)的问题,,⽽如果打war包部署到tomcat则没问题,可以正常使⽤,经过排查很久,出问题所在:
因为jar打包封装后是不能改变其内部⽬录结构的,也就是说,按⽇期分类的⽂件上传⽂件夹,如果当需要创建新⽇期的⽂件夹的时候,是⽆法在jar包中新增⽂件夹的,这时候就会出现IO异常问题。⽽对于放在tomcat中的war包,当tomcat运⾏的时候会⾃动解压war包,其在服务器上是存在真实路径的。
解决⽅案
⽅案⼀:我在⽹上了⼀种⽅法,是通过打完jar包部署后,给springboot项⽬static下的⽂件上传⽂件夹单独分离出来(相当于是以相对路径换绝对路径),访问的时候直接相当通过服务器上和jar包同级⽬
录下新建⼀个⽂件上传⽂件夹。
⽅案⼆:直接将⽂件上传到服务器指定路径下的⽂件上传位置,这种⽅式也相当于直接使⽤绝对路径。
⽅案三:在服务器上使⽤FastDFS和Nginx搭建分布式⽂件存储,这种⽅式⽐较复杂,⽽且学⽣及本来内存和带宽就⼩,在⾃⼰电脑的虚拟机可以试试这种⽅案,还是挺好⽤的,学⽣服务器就算了。
⽅案四:就是直接将⽂件上传到阿⾥云OSS⽂件存储系统上
2. 阿⾥云OSS购买和配置
这个⽐较简单,给⼤家推荐⼀篇博⽂⾃⼰了解下,也可以参考。
3. Springboot操作OSS
创建⼀个spring boot项⽬,pom⽂件需要引⼊依赖:
<dependencies>
<!--
个⼈版本踩坑:
不加这个依赖的话,当在配置类中
使⽤@ConfigurationProperties(prefix = "aliyun")注解时,
我这个版本的spring boot会提⽰有问题
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- swagger ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- thymeleaf 可不加,个⼈习惯性引⼊ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 热部署,看个⼈习惯 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- ⼩辣椒插件,推荐使⽤,可以节省javaBean的setter/getter,还可以使⽤链式调⽤ -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- fastJson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<!-- aliyun-oos -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
<!-- apache-common-lang3 -->
<dependency>
<groupId>org.apachemons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
包结构很简单:
我们使⽤⾃⼰添加的application-aliyun-oss.properties配置⽂件,去配置OSS相关信息,之所以不在l 中配置,看个⼈习惯了,因为⾃定义的配置属性还是提出来配⽐较好,没必要所有的都配到l(properties)中去。
application-aliyun-oss.properties
# ⽂件上传⼤⼩限制
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=1000MB
# 地域节点
# Bucket 域名
aliyun.urlPrefix=csp-xxxx.oss-cn-beijing.aliyuncs/
# accessKey Id
aliyun.accessKeyId=LTAI4XXXXXXXzqD1saGFZ
# accessKey Secret
aliyun.accessKeySecret=2WjxNXXXXXXXX4f2bREc
# 你的Bucket名称
aliyun.bucketName=csp-xxxx
# ⽬标⽂件夹
aliyun.fileHost=files
config包下的相关配置类
AliyunOssConfig.java
/**
* @Auther: csp1999
* @Date: 2020/10/31/13:33
* @Description: 阿⾥云 OSS 基本配置
*/
// 声明配置类,放⼊Spring容器
@Configuration
// 指定配置⽂件位置
@PropertySource(value = {"classpath:application-aliyun-oss.properties"})
// 指定配置⽂件中⾃定义属性前缀
@ConfigurationProperties(prefix = "aliyun")
@Data// lombok
@Accessors(chain = true)// 开启链式调⽤
public class AliyunOssConfig {
private String endPoint;// 地域节点
private String accessKeyId;
private String accessKeySecret;
private String bucketName;// OSS的Bucket名称
private String urlPrefix;// Bucket 域名
private String fileHost;// ⽬标⽂件夹
// 将OSS 客户端交给Spring容器托管
@Bean
public OSS OSSClient() {
return new OSSClient(endPoint, accessKeyId, accessKeySecret);
}
}
Swagger2Config.java
/**
* @Auther: csp1999
* @Date: 2020/10/31/16:30
* @Description: Swagger 配置类
*/
@Configuration
@EnableSwagger2// 开启swagger2
public class Swagger2Config {
@Bean
public Docket webApiConfig() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.(("/error.*")))
.build();
}
private ApiInfo webApiInfo() {
return new ApiInfoBuilder()springboot结构
.
title("SpringBoot整合OSS-API⽂档")
.description("阿⾥云OSS-⽂件上传下载测试")
.version("1.0")
.contact(new Contact("CSP", "blog.csdn/weixin_43591980", ""))
.build();
}
}
定义⼀个关于执⾏状态结果的枚举类
/**
* @Auther: csp1999
* @Date: 2020/10/31/17:03
* @Description: 状态码枚举类
*/
public enum StatusCode {
SUCCESS("success",200),ERROR("error",500);
private String msg;
private Integer code;
StatusCode(String msg,Integer code){
this.msg = msg;
}
StatusCode(Integer code){
}
StatusCode(String msg){
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
}
}
service层
在service使⽤ossClient操作阿⾥云OSS,进⾏上传、下载、删除、查看所有⽂件等操作,同时可以将图⽚的url进⾏⼊库操作:FileUploadService.java
/**
* @Auther: csp1999
* @Date: 2020/10/31/14:30
* @Description: ⽂件上传Service (为节省⽂章中的代码篇幅,不再做接⼝实现类处理)
*/
@Service("fileUploadService")
public class FileUploadService {
// 允许上传⽂件(图⽚)的格式
private static final String[] IMAGE_TYPE = new String[]{".bmp", ".jpg",
".jpeg", ".gif", ".png"};
@Autowired
private OSS ossClient;// 注⼊阿⾥云oss⽂件服务器客户端
@Autowired
private AliyunOssConfig aliyunOssConfig;// 注⼊阿⾥云OSS基本配置类
/*
* ⽂件上传
* 注:阿⾥云OSS⽂件上传官⽅⽂档链接:help.aliyun/document_detail/84781.html?spm=a2c4g.11186623.6.749.11987a7dRYVSzn
* @param: uploadFile
* @return: string
* @create: 2020/10/31 14:36
* @author: csp1999
*/
public String upload(MultipartFile uploadFile) {
// 获取oss的Bucket名称
String bucketName = BucketName();
// 获取oss的地域节点
String endpoint = EndPoint();
// 获取oss的AccessKeySecret
String accessKeySecret = AccessKeySecret();
// 获取oss的AccessKeyId
String accessKeyId = AccessKeyId();
// 获取oss⽬标⽂件夹
String filehost = FileHost();
// 返回图⽚上传后返回的url
String returnImgeUrl = "";
/
/ 校验图⽚格式
boolean isLegal = false;
for (String type : IMAGE_TYPE) {
if (OriginalFilename(), type)) {
isLegal = true;
break;
}
}
if (!isLegal) {// 如果图⽚格式不合法
return Msg();
}
/
/ 获取⽂件原名称
String originalFilename = OriginalFilename();
// 获取⽂件类型
String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
// 新⽂件名称
String newFileName = UUID.randomUUID().toString() + fileType;
// 构建⽇期路径, 例如:OSS⽬标⽂件夹/2020/10/31/⽂件名
String filePath = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
// ⽂件上传的路径地址
String uploadImgeUrl = filehost + "/" + filePath + "/" + newFileName;
// 获取⽂件输⼊流
InputStream inputStream = null;
try {
inputStream = InputStream();
} catch (IOException e) {
e.printStackTrace();
}
/**
* 下⾯两⾏代码是重点坑:
* 现在阿⾥云OSS 默认图⽚上传ContentType是image/jpeg
* 也就是说,获取图⽚链接后,图⽚是下载链接,⽽并⾮在线浏览链接,
* 因此,这⾥在上传的时候要解决ContentType的问题,将其改为image/jpg
*/
ObjectMetadata meta = new ObjectMetadata();
meta.setContentType("image/jpg");
//⽂件上传⾄阿⾥云OSS
ossClient.putObject(bucketName, uploadImgeUrl, inputStream, meta);
/**
* 注意:在实际项⽬中,⽂件上传成功后,数据库中存储⽂件地址
*/
// 获取⽂件上传后的图⽚返回地址
returnImgeUrl = "" + bucketName + "." + endpoint + "/" + uploadImgeUrl;
return returnImgeUrl;
}
/*
* ⽂件下载
* @param: fileName
* @param: outputStream
* @return: void
* @create: 2020/10/31 16:19
* @author: csp1999
*/
public String download(String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
// // 设置响应头为下载
// response.setContentType("application/x-download");
// // 设置下载的⽂件名
// response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
// response.setCharacterEncoding("UTF-8");
// ⽂件名以附件的形式下载
response.setHeader("Content-Disposition", "attachment;filename=" + de(fileName, "UTF-8"));
// 获取oss的Bucket名称
String bucketName = BucketName();
// 获取oss⽬标⽂件夹
String filehost = FileHost();
// ⽇期⽬录
// 注意,这⾥虽然写成这种固定获取⽇期⽬录的形式,逻辑上确实存在问题,但是实际上,filePath的⽇期⽬录应该是从数据库查询的 String filePath = new DateTime().toString("yyyy/MM/dd");
String fileKey = filehost + "/" + filePath + "/" + fileName;
// ossObject包含⽂件所在的存储空间名称、⽂件名称、⽂件元信息以及⼀个输⼊流。
OSSObject ossObject = Object(bucketName, fileKey);
try {
// 读取⽂件内容。
InputStream inputStream = ObjectContent();
BufferedInputStream in = new BufferedInputStream(inputStream);// 把输⼊流放⼊缓存流
ServletOutputStream outputStream = OutputStream();
BufferedOutputStream out = new BufferedOutputStream(outputStream);// 把输出流放⼊缓存流
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
if (out != null) {
out.flush();
out.close();
}
if (in != null) {
in.close();
}
return Msg();
} catch (Exception e) {
return Msg();
}
}
/*
* ⽂件删除
* @param: objectName
* @return: java.lang.String
* @create: 2020/10/31 16:50
* @author: csp1999
*/
public String delete(String fileName) {
// 获取oss的Bucket名称
String bucketName = BucketName();
// 获取oss的地域节点
String endpoint = EndPoint();
// 获取oss的AccessKeySecret
String accessKeySecret = AccessKeySecret();
// 获取oss的AccessKeyId
String accessKeyId = AccessKeyId();
// 获取oss⽬标⽂件夹
String filehost = FileHost();
// ⽇期⽬录
// 注意,这⾥虽然写成这种固定获取⽇期⽬录的形式,逻辑上确实存在问题,但是实际上,filePath的⽇期⽬录应该是从数据库查询的 String filePath = new DateTime().toString("yyyy/MM/dd");
try {
/**
* 注意:在实际项⽬中,不需要删除OSS⽂件服务器中的⽂件,
* 只需要删除数据库存储的⽂件路径即可!
*/
// 建议在⽅法中创建OSSClient ⽽不是使⽤@Bean注⼊,不然容易出现Connection pool shut down
OSSClient ossClient = new OSSClient(endpoint,
accessKeyId, accessKeySecret);
// 根据BucketName,filetName删除⽂件
// 删除⽬录中的⽂件,如果是最后⼀个⽂件fileoath⽬录会被删除。
String fileKey = filehost + "/" + filePath + "/" + fileName;
ossClient.deleteObject(bucketName, fileKey);
try {
} finally {
ossClient.shutdown();
}
System.out.println("⽂件删除!");
return Msg();
} catch (Exception e) {
e.printStackTrace();
return Msg();
}
}
}
controller层
controller提供测试接⼝
/**
* @Auther: csp1999
* @Date: 2020/10/31/16:40
* @Description: OSS ⽂件上传controller
*/
@Api(description = "阿⾥云OSS⽂件上传、下载、删除API")
@RequestMapping("api/pri/file")
@RestController
public class OssFileController {
@Autowired
private FileUploadService fileUploadService;
/*
* ⽂件上传api
* @param: file
* @return: com.alibaba.fastjson.JSONObject
* @create: 2020/10/31 17:35
* @author: csp1999
*/
@ApiOperation(value = "⽂件上传")
@PostMapping("upload")
public JSONObject upload(@RequestParam("file") MultipartFile file) {
JSONObject jsonObject = new JSONObject();
if (file != null) {
String returnFileUrl = fileUploadService.upload(file);
if (returnFileUrl.equals("error")) {
jsonObject.put("error", "⽂件上传失败!");
return jsonObject;
}
jsonObject.put("success", "⽂件上传成功!");
jsonObject.put("returnFileUrl", returnFileUrl);
return jsonObject;
} else {
jsonObject.put("error", "⽂件上传失败!");
return jsonObject;
}
}
/*
* ⽂件下载api
* @param: fileName
* @param: response
* @return: com.alibaba.fastjson.JSONObject
* @create: 2020/10/31 17:35
* @author: csp1999
*/
@ApiOperation(value = "⽂件下载")
@GetMapping(value = "download/{fileName}")
public JSONObject download(@PathVariable("fileName") String fileName, HttpServletResponse response) throws Exception { JSONObject jsonObject = new JSONObject();
String status = fileUploadService.download(fileName, response);
if (status.equals("error")) {
jsonObject.put("error", "⽂件下载失败!");
return jsonObject;
} else {
jsonObject.put("success", "⽂件下载成功!");
return jsonObject;
}
}
/*
* ⽂件删除api
* @param: fileName
* @return: com.alibaba.fastjson.JSONObject
* @create: 2020/10/31 17:35
* @author: csp1999
*/
@ApiOperation(value = "⽂件删除")
@GetMapping("/delete/{fileName}")
public JSONObject DeleteFile(@PathVariable("fileName") String fileName) {
JSONObject jsonObject = new JSONObject();
String status = fileUploadService.delete(fileName);
if (status.equals("error")) {
jsonObject.put("error", "⽂件删除失败!");
return jsonObject;
} else {
jsonObject.put("success", "⽂件删除成功!");
return jsonObject;
}
}
}
4.运⾏项⽬测试API接⼝
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论