文章目录
一、需求分析
在博客项目的前台中,需要完成对个人资料的更新,这其中包含对用户头像的更新,其实也就是替换掉原来的头像,另外在后台中需要在写博文中对缩略图以及文章正文中图片的添加,以及能对博文的缩略图以及正文中的图片进行修改。
二、设计思路
先来谈一下原来的处理方式,进而引出为什么现在需要这种处理方式。
因为之前做过锋迷商城这个项目,所以还是有在数据库中存图片地址的这种意识,只不过那个项目比较拉,图片都在前端项目里,当然咯,那个项目只是涉及到前台,也就是说仅仅是图片的显示,直接从数据库中查询到图片地址,然后到前端项目中去找到对应的图片即可,没有涉及到后台,也就是没有涉及到对图片的上传,另外它这个项目前台中,也没有修改图片之类的,仅仅只是单纯的对图片进行显示,看似也没有多大问题,图片存死也没啥问题,但是一旦需要扩充就比较麻烦了,需要手动的往前端项目中添加图片。另外如果我此时需要上传图片呢,如果是在前端工程中将图片写死,那么我就只能上传前端项目中的图片,因为只有这样地址才是有效的,因为你如果上传的是其它地方的图片,图片地址在前端项目中里面找不到,即使可能可以通过你选择本地图片,然后将图片保存到前端项目中,然后再返回图片地址。
其实这里总结来说在锋迷商城项目中是将图片存到前端工程里。
存到前端工程里面,当然有其优点,比如说:
1. 图片的加载速度更快,因为是直接从本地获取,而不需要访问网络。
2. 安全性高,因为图片存储到本地,没有外界能直接访问图片的接口。
3. 可靠性高,因为存储到本地,不会受到网络,以及服务器故障的影响。
总的来说优点有三点:加载快、安全、稳定。
但其缺点也很明显,就比如:
1. 存储空间受限,因为是存储到本地,所以可能会受到存储空间的限制。
2. 难以扩展,因为受到存储空间的限制,因此当面对大量的图片时,可能需要修改存储位置以及修改代码。
3. 可维护性差,因为存储到前端工程中,本质上是存储到本地磁盘中,所以就不能保证唯一的文件地址可以生效了,就比如说我的前端工程,到其它电脑上面,路径可能就会失效,就比如说在我的电脑上面地址是,c/前端项目/images自然来说项目中代码也是这个,虽然可能会在代码中写相对路径,代码中地址是/images,可是另外那台电脑连c/前端工程这个目录都没有的话,就找不到对应的文件了,因此如果存在多个开发者,就需要多交流沟通,遵循路径规范,增加维护难度。
那么如果将文件存到云端,就能解决这些问题了。
1、因为存储到云端,因为云端一般来说有着巨大的存储空间,因此一般来说不会因为存储空间的问题而发愁。
2、存储到云端后,云端会响应回来一个唯一的URL地址,无论在哪台电脑上面都能正确访问。
当然也是有缺点的,存到本地的优点的反面就是存到云端的缺点,就比如说:
1. 因为需要访问网络,可能文件的加载速度就相较于本地会慢一些。
2. 风险高了,因为存到了云端,因此存在着访问接口,因此安全性也就低一些,需要在云端,做好安全措施。
3. 因为和云服务器挂钩,因此可能会受到云服务器的影响,比如说云服务器发生故障,或者网络不好等问题。
对比了它两,但是总的来说保存到本地的缺点明显,保存的云端的优势明显,即使存在问题,也可以通过一些措施进行预防和解决。
好了,正是比较了它两,得知了存储到云端的优点,我才会选择将图片保存到云端,保存到云端也很多厂商都可以做到,七牛云、阿里云、腾讯云都可以,其实这个技术就叫做OSS(对象存储服务),我还是比较喜欢使用阿里云的,下面我就来基于阿里云的OSS,如何实现文件的上传功能,当然这里不仅可以是文件的上传,也可以是文件的下载,以及文件夹的创建等许多需求,具体实现步骤可以参考阿里云的文档。
三、实现步骤
3.1 预处理:先在官网上,完成OOS的配置
3.1.1 购买OSS服务。
使用阿里云OSS之前,肯定得先到阿里云官网,购买OSS服务,这是使用任何服务的第一步。进入到阿里云OSS页面的步骤如下图所示:
然后选择立即购买:
然后就是规格的选择,第一次用的小伙伴就可以完全按照我下面的配置进行选择,这种配置完全够简单开发了,如果以后还有另外的需求,查阅阿里云的文档,进行配置即可,其实也就是默认配置,看得出其实OSS服务还是挺便宜的,半年才几块钱。
3.1.2 创建Bucket
Bucket其实就是文件盘,相当于电脑上面的C盘、D盘,里面可以放各式各样的文件,文件夹,当然这一步也可以通过Java程序进行创建,具体创建步骤翻阅开发文档,其实没必要,我这里就需要一个现成的盘,往里面存图片就行了。因此创建一个Bucket,步骤如下:
1. 先进入OSS的控制台页面,然后按如下步骤,先找到创建Bucket的入口
3.1.3 Bucket参数的配置
除了我通过序号进行说明的,其它配置不需要管,默认的就行
3.2 Java程序中使用OSS:查询开发文档
3.2.1 在控制台页面,找到OSS的Java开发文档
3.2.2 在中央仓库找到OSS坐标
将坐标复制到maven中
3.2.3 查询文档,了解基本操作
3.2.4 参数的获取(AK参数的获取)
其实这个和oss本身没有关系,只是调用接口的时候,都需要凭证才能进行调用,完成相关操作。
下面我来说一下,AK的获取:
1. 可以通过本身自己root用户进行创建获取
也可以通过RAM子用户进行AK的创建
其实就是一个账号,可以有多个用户,一个主用户,多个分用户,为了安全起见,可以通过使用分用户来创建AK,并对这个分用户提供较少的权限,例如在这里我只需要给RAM用户提供操作oss服务的权限就行了,当然一个用户也可以有多个AK。
下面是RAM用户的创建步骤:
最重要的AK两个值的获取:
如果错过了,没有保存,只能重新创建一个AK:
为当前RAM用户设置权限:
3.2.5 制作自己的操作OSS的工具类
1. 先是属性配置类的创建,使得能在application.yml文件中,对oss所需的一些参数进行配置
@Component
@Data
@ConfigurationProperties(prefix = "oss")
public class OSSProperty {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
在application.yml文件中,完成配置:
oss:
endpoint:
accessKeyId:
accessKeySecret:
bucketName:
2. 工具类的封装
@Component
public class OSSUtils {
@Autowired
private OSSProperty ossProperty;
//创建Bucket
public void createBucket(String bucketName){
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = ossProperty.getEndpoint();
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = ossProperty.getAccessKeyId();
String accessKeySecret = ossProperty.getAccessKeySecret();
// 填写Bucket名称,例如example bucket。
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
// 创建存储空间。
ossClient.createBucket(bucketName);
} 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();
}
}
}
//上传文件
public String uploadFile(MultipartFile file){
String bucketName = ossProperty.getBucketName();
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = ossProperty.getEndpoint();
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = ossProperty.getAccessKeyId();
String accessKeySecret = ossProperty.getAccessKeySecret();
String fileName = "";
fileName = file.getOriginalFilename();
String url = "";
String path = PathUtils.generateFilePath(fileName);
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
ossClient.putObject(bucketName, path, file.getInputStream());
url = "https://"+bucketName+"."+endpoint.substring(8) + "/" + path;
return 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());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return "";
}
}
关键的三步:
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//上传文件关键代码
//提供bucket名称、上传之后存到OSS中的文件路径、文件输入流
ossClient.putObject(bucketName, path, file.getInputStream());
最后文件的云地址:
//文件的云地址
url = "https://"+bucketName+"."+endpoint.substring(8) + "/" + path;
这是参看文件列表中文件的文件详情发现的,地址格式
另外这里为了使得保存在OSS中的位置显得有层次性,并且路径唯一,对于文件地址的表示,采用当前时间(年/月/日的格式)+ UUID + 后缀名的方式进行表示。下面是路径生成的工具类:
public class PathUtils {
public static String generateFilePath(String fileName){
//根据日期生成路径 2022/1/15/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
String datePath = sdf.format(new Date());
//uuid作为文件名
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
//后缀和文件后缀一致
int index = fileName.lastIndexOf(".");
// test.jpg -> .jpg
String fileType = fileName.substring(index);
return new StringBuilder().append(datePath).append(uuid).append(fileType).toString();
}
}
3.2.6 编写上传文件接口
controller层:
@RestController
@Api(tags = "上传图片",description = "完成图片上传接口")
public class UploadController {
@Autowired
private UploadImgService uploadImgService;
@PostMapping("/upload")
@LogPrint(BusinessName = "上传图片接口")
@ApiOperation(value = "上传图片",notes = "上传图片到云存储器")
@ApiImplicitParam(name = "img", value = "图片文件", required = true)
public ResponseResult uploadImg(MultipartFile img){
try {
return uploadImgService.uploadImg(img);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("文件上传失败");
}
}
}
service层:
service接口:
public interface UploadImgService {
ResponseResult uploadImg(MultipartFile img);
}
service实现类:
主要就是对文件类型的检查,虽然前端已经做了防护,但为了防止懂技术的人拿到后端接口直接调用,因此还得检查一遍,然后就是直接调用工具类的上传文件方法即可。
@Service
public class UploadImgServiceImpl implements UploadImgService {
@Autowired
private OSSUtils ossUtils;
@Override
public ResponseResult uploadImg(MultipartFile img) {
String imgName = img.getOriginalFilename();
if (!imgName.endsWith("jpg") && !imgName.endsWith("png")) {
throw new SystemException(AppHttpCodeEnum.FILE_TYPE_ERROR);
}
return ResponseResult.okResult(ossUtils.uploadFile(img));
}
}
最后将返回的文件的云地址,存入到数据库就行了。
四、总结
1. 慢慢的增强查询文档的能力,这一次的OSS的使用,其实也是锻炼查询文档的能力。
2. OSS的具体使用,可以简化成以下几步:
1. 购买OSS服务,创建好Bucket
2. 获取AK(可以通过root或者ARM用户进行获取)
3. 在程序中完成oss所需的参数配置包括AK参数的配置,以及上传文件功能的封装