前言
不知道大家有没有过这样一个开发经历,用户可以从前端上传自己的头像,头像上传到服务器后保存到数据库,然后在前端显示头像。在我还没有意识到前端显示图片需要图片的URL地址时,我完成了前端选择头像上传,然后后端保存图片,将图片名直接返回给前端,希望前端能够展示头像。但是,我折腾了好久才发现,想要展示图片,需要服务器的加入。首先,需要获取到前端传过来的文件(图片或者其他文件),然后将其保存到服务器(或者专门用于存储文件的存储云服务),保存到服务器后,会返回一个URL地址,然后将该URL地址保存到数据库中,返回给前端,前端就可以展示头像。
所以,这里要有一个观念的转换,图片是保存到服务器中的,而不是保存到数据库中的,数据库中只是保存了图片地址而已。
ps:前端上传文件可进入传送门:前端 vue + vant 实现文件上传-CSDN博客
OSS云存储服务
这里重点介绍用法
官网:
介绍
阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。
使用
前提条件
需要java环境在1.7.0版本以上,查看java版本命令
java -version
下载SDK
地址:https://github.com/aliyun/aliyun-oss-java-sdk/releases
建议下载最新版本SDK,下载到本地即可
安装依赖
如果你的项目是Maven项目直接将上一步下载的SDK中的pom.xml文件中的maven地址拷贝到自己的项目中即可
下图是我下载的SDK版本中的部分pom.xml文件,只需要将红框中的内部拷贝到自己的项目中即可
另外。对于java9以上的版本,还需要添加JAXB相关依赖如下:
<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>
配置访问凭证
有两种访问凭证,一种是临时的,一种是长久的,如果对于安全性要求较高,那么就选择临时的,如果对于安全性要求不高,那么可以选择长期的,这里我选择的是长期的凭证(windows,其他操作系统可以参照上面给出的官方地址)
步骤如下:
获取RAM用户的访问密钥
什么是RAM用户?看文档去吧:什么是RAM用户_访问控制(RAM)-阿里云帮助中心,使用OSS的前提是你已经有了阿里云账号,并且已经创建了RAM用户,如果你还没有前置操作,可以跟着文档去操作,花个半小时左右就搞定。
这一步需要使用到的密钥就是你创建好RAM用户后平台自动给你生成的,访问密钥AccessKey(简称AK)是阿里云提供给阿里云用户的永久访问密钥,用于通过开发工具(API、CLI、SDK、Cloud Shell、Terraform等)访问阿里云时的身份验证,不用于控制台登录。AccessKey包括AccessKey ID和AccessKey Secret,需要一起使用,需要妥善保管
配置访问密钥
获取到自己的访问密钥后,接下来就需要配置访问密钥了
1. 首先配置环境变量
在命令行窗口中输入以下命令:
set OSS_ACCESS_KEY_ID=LTAI4GDty8ab9W4Y1D**** // 你的appid
set OSS_ACCESS_KEY_SECRET=IrVTNZNy5yQelTETg0cZML3TQn**** // 你的appsecret
执行以下命令使更改生效
setx OSS_ACCESS_KEY_ID "%OSS_ACCESS_KEY_ID%"
setx OSS_ACCESS_KEY_SECRET "%OSS_ACCESS_KEY_SECRET%"
执行命令验证环境变量配置
echo %OSS_ACCESS_KEY_ID%
echo %OSS_ACCESS_KEY_SECRET%
如果配置成功,则返回如下:
LTAI4GDty8ab9W4Y1D****
IrVTNZNy5yQelTETg0cZML3TQn****
注意:这里可能会遇到小坑,以上命令执行完之后,建议重新启动电脑,以确保配置生效,否则后端运行时可能会报类似于密钥为空之类的错
2. 然后就是在代码中从环境变脸中获取到你的密钥(这行代码在后续代码还会讲到,这里先不用管)
// 使用环境变量中获取的RAM用户的访问密钥配置访问凭证。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
编写工具类
这部分就是无脑复制粘贴,以下代码是上传文件(图片)用的,可以直接赋值粘贴到你的项目中,然后就可以使用
package com.hitsz.badboyHelm.utils;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.UUID;
@Component
public class OSSutils {
// 访问域名,参照官方地址:https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints?spm=a2c4g.11186623.0.i1#concept-zt4-cvy-5db
@Value("${aliyun-oss.endpoint}")
private String endpoint;
// 存储空间名称,在OSS的控制台上创建存储空间时会得到。官方地址:https://oss.console.aliyun.com/bucket
@Value("${aliyun-oss.bucketName}")
private String bucketName;
public String uploadFileToOSS(MultipartFile file) throws Exception{
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 获取上传文件的输入流
InputStream inputStream = file.getInputStream();
// 使用UUID通用唯一识别码 + 后缀名的形式,方式文件名重复导致覆盖
String orignalFilename = file.getOriginalFilename();
// 增加断言null值抛出异常
assert orignalFilename != null;
String fileName = UUID.randomUUID().toString() + orignalFilename.substring(orignalFilename.lastIndexOf("."));
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = fileName;
// 文件上传到服务器之后的文件访问路径
String url = "";
try {
ossClient.putObject(bucketName, objectName, inputStream);
//拼接文件访问路径并返回 在endpoint名称中加入bucket名称 最后拼接上文件名
url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
} 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();
}
}
return url;
}
}
注意:代码中的url就是图片保存到阿里云存储服务中后返回的地址,需要将这个url地址保存到数据库中
代码中,enpoint,backetName 是需要配置在配置文件中的(也可以直接写在代码中,只是违背了开闭原则,因为以后如果需要修改这两个信息,就必须要修改代码,如果写在配置文件中,就不需要修改代码),如下:
#OSS配置
aliyun-oss:
endpoint: https://oss-cn-shenzhen.aliyuncs.com
bucketName: badboy-helm-img
对enpoint 和 backetName 不熟悉的也可以去看文档,
enpoint :访问域名和数据中心_对象存储(OSS)-阿里云帮助中心
backetName:阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台
实操
配置好之后,就可以对前端传过来的文件进行上传了,示例代码如下(service层):
@Autowired
private OSSutils osSutils;
@PostMapping("/handleUpdataAvatar")
public BaseResponse<User> updateUserAvatarById(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception{
// 调用封装的OSS工具类将文件上传到OSS
String url = osSutils.uploadFileToOSS(file);
// 获取当前用户
User loginUser = userService.getLoginUser(request);
// 更新用户头像
loginUser.setUserAvatar(url);
boolean result = userService.updateById(loginUser);
// 将更新完的用户信息返回
User user = userService.getById(loginUser.getId());
// 对用户信息进行脱敏然后返回
return ResultUtils.success(userService.getSafetyUser(user));
}
扩展
如果需要接受前端传过来的多个键值对组成的formData对象(表单数据),接受时,只需要创建一个bean对象,bean对象中的字段与前端传过来的键值对中的键一一对应即可,示例代码如下:
javaBean对象
// 字段名与前端传过来的字段名一致
@Data
public class AddRoleRequest implements Serializable {
/**
* 角色真名
*/
private String roleName;
/**
* 角色昵称
*/
private String roleNickName;
/**
* 角色简介
*/
private String roleProfile;
/**
* 角色头像,类型为MultipartFile,因为前端是以表单数据形式传递过来的
*/
private MultipartFile roleAvatar;
/**
* 角色标签
*/
private List<String> tags;
}
controller层:
@PostMapping("/add")
public BaseResponse<Role> addRole2(AddRoleRequest addRoleRequest, HttpServletRequest request){
if(addRoleRequest == null){
throw new BusinessException(ErrorCode.OPERATION_ERROR);
}
// 获取当前用户
User loginUser = userService.getLoginUser(request);
Role role = roleService.addRole(addRoleRequest, loginUser);
return ResultUtils.success(role);
}
需要注意的是:请求方式必须是post,并且不能使用@RequestBody来接收参数,否则会报错。
好了,后端接收前端传过来的文件并保存到服务器然后返回url给前端就已经完成了。如果有什么问题,欢迎向我提问,看到提问之后会及时解答!
-----------------------------------------------------------------------------------------------------------------------------
文章中只有后端保存文件部分,对于前端上传文件可以进主页查看!