大体思路
上传
前端上传MultipartFile file 文件
进行名字空值校验和格式校验,大概就是判断后缀是不是属于jpg.png
生成唯一uuid名称,然后拿着这个文件名和图片文件File调接口
接口参数为
- 输入流inputstream,将file化流传输
- 文件名
- 上传token,由access,secretKey,bucket生成,还可以指定过期时间,其他额外配置,该配置我们单独搞一个配置类注册bean后续注入即可
完事之后拼接url,https(fileAccess)+www.zzz.cn/47.11.11.1(domain)+文件名path丢给前端展示
这里上传失败还可以进行重重
删除
传文件名,bucket,调接口
代码如下
依赖
<!-- 七牛云依赖-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.2.28</version>
</dependency>
application.yml格式
# ========================== ↓↓↓↓↓↓ 七牛云配置 ↓↓↓↓↓↓ ==========================
qiniu:
accessKey: xxxx
secretKey: xxxx
# 对象储存
bucket: xxx # 空间名称
zone: huanan # 存储区域
domain: cdn.988.cn # 访问域名
fileAccess: https:// 没有ssl认证就http://
具体实现
service
package com.zww.service;
import com.qiniu.common.QiniuException;
import org.springframework.web.multipart.MultipartFile;
/**
* @author pangpi
*/
public interface FileService {
/**
* 上传图片到OOS
* @param img 上传的图片文件
* @return 返回图片的链接路径
*/
String uploadImg(MultipartFile img);
String deleteImg(String key) throws QiniuException;
}
serviceImpl
package com.zww.service.impl;
import cn.hutool.core.util.ObjUtil;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.zww.common.exception.ServiceException;
import com.zww.framework.enums.AppHttpCodeEnum;
import com.zww.framework.enums.FileTypeEnum;
import com.zww.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author pangpi
*/
@Slf4j
@Service
public class FileServiceImpl implements FileService {
// @Autowired
// private ApplicationContext applicationContext;
@Value("${qiniu.fileUploadType}")
private String fileUploadType;
@Value("${qiniu.accessKey}")
private String accessKey;
@Value("${qiniu.secretKey}")
private String secretKey;
@Value("${qiniu.bucket}")
private String bucket;
@Value("${qiniu.domain}")
private String domain;
@Value("${qiniu.fileAccess}")
private String fileAccess;
// akey和skey在里面定义好了
@Autowired
private Auth auth;
// 地区configuration在里面通过读取application.yml初始化好了
@Autowired
private UploadManager uploadManager;
@Autowired
private BucketManager bucketManager;
// 这个方法就是在加工唯一文件名和校验用户上传的是否是图片格式后缀例如jpg,png
@Override
public String uploadImg(MultipartFile img) {
// 判断文件类型,和文件大小
// 获取原始文件名
String originalFilename = img.getOriginalFilename();
if (originalFilename == null) {
throw new ServiceException("文件名不能为空");
// throw new ServiceException(AppHttpCodeEnum.FILL_NAME_NOT_NULL.getMsg());
}
// 获取文件后缀
String fileSuffix = getFileSuffix(originalFilename);
// 判断后缀是否匹配img
// if (checkImageSuffix(fileSuffix, FileTypeEnum.IMG.getFileType())) {
if (checkImageSuffix(fileSuffix, "image")) {
// throw new ServiceException(AppHttpCodeEnum.FILE_TYPE_ERROR.getMsg());
throw new ServiceException("上传类型格式不为图片jpg,png...");
}
// 通过文件获取生成的文件路径
// 根据日期生成路径 2022-1-15-
// new一个日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-");
// 通过格式器转换一个时间
String datePath = sdf.format(new Date());
// uuid作为文件名
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
// 后缀和文件后缀一致
int index = originalFilename.lastIndexOf(".");
// test.jpg -> .jpg
String fileType = originalFilename.substring(index);
String filePath = datePath + uuid + fileType;
// 就是从test.jpg变成了2024-9-1-uuid32142343.jpg
String url = uploadOss(img, filePath);
return url;
}
// 传入文件名然后调接口
@Override
public String deleteImg(String key) throws QiniuException {
// 由于bucketManager注入了spring,且初始化了a,skey,和地区,所以可以直接传bucket调用
// 如果有重传需求则三次重传
Response response = bucketManager.delete(this.bucket, key);
int retry = 0;
while (response.needRetry() && retry++ < 3) {
response = bucketManager.delete(this.bucket, key);
}
return response.statusCode == 200 ? "删除成功!" : "删除失败!";
}
/**
* @param file 文件
* @param filePath 文件路径
* @return 文件访问url
*/
// 如果你有别的oos,那么这里会动态获取bean并且上传
// OosUploadService oosUploadService = applicationContext.getBean(fileUploadType + "_oos", OosUploadService.class);
// if (ObjUtil.isNotEmpty(oosUploadService)) {
// return oosUploadService.uploadOss(file, filePath);
// } else {
// log.error("对象存储 {} 不存在,请联系管理员",fileUploadType);
// throw new ServiceException(AppHttpCodeEnum.SYSTEM_ERROR.getMsg());
// }
// 这里直接使用qiniu_oss了,上传成功后返回字符串
private String uploadOss(MultipartFile file, String filePath) {
// 这里的getUploadToken()是获取上传凭证,由
// Access Key: 访问密钥
// Secret Key: 秘密密钥 和 存储空间名 bucket所生成
// 同时还设置了令牌过期时间sxpire
// 这两个密钥用于身份验证和权限控制,确保只有授权的用户可以访问或操作资源
// 获取文件输入流
try {
// 将文件转为输入流,后面流给七牛云
InputStream inputStream = file.getInputStream();
try {
//上传ing...
Response response = this.uploadManager.put(inputStream, filePath, getUploadToken(), null, null);
// 如果上一步执行没问题则说明上传完毕,此时给链接前端展示即可
int retry = 0;
// 如果有重传需求则三次重传
while (response.needRetry() && retry < 3) {
response = this.uploadManager.put(inputStream, filePath, getUploadToken(), null, null);
retry++;
}
if (response.statusCode == 200) {
return fileAccess + domain + "/" + filePath;
}
return "上传失败";
} catch (QiniuException e) {
// 如果上传失败,捕获QiniuException并打印错误信息。
System.err.println(e.response.toString());
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 获取上传凭证
*/
private String getUploadToken() {
return this.auth.uploadToken(bucket, null, 3600, null);
}
/**
* 输入文件名,获取文件的后缀
*
* @param fileName 文件名
* @return 文件后缀
*/
private String getFileSuffix(String fileName) {
// 拆分文件和后缀
String[] split = fileName.split("\\.");
if (split.length <= 1) {
throw new ServiceException(AppHttpCodeEnum.FILL_NAME_NOT_SUFFIX.getMsg());
}
return split[split.length - 1];
}
/**
* 根据传入的后缀和文件类型,判断其后缀是否正常
*
* @param imgSuffix 文件类型
* @return 是否满足后缀要求
*/
private boolean checkImageSuffix(String imgSuffix, String fileType) {
// 暂时使用set进行判断,后续有需要改为数据库动态存储不同文件类型下的文件后缀
Map<String, Set<String>> fileTypeSuffixMap = new HashMap<>();
// 每个文件类型存储多个后缀格式用set存放,最后放入map
Set<String> imageSuffixName = new HashSet<>();
imageSuffixName.add("jpg");
imageSuffixName.add("jpeg");
imageSuffixName.add("jpe");
imageSuffixName.add("png");
imageSuffixName.add("svg");
imageSuffixName.add("webp");
fileTypeSuffixMap.put(FileTypeEnum.IMG.getFileType(), imageSuffixName);
Set<String> excelSuffixName = new HashSet<>();
excelSuffixName.add("xlsx");
excelSuffixName.add("xls");
fileTypeSuffixMap.put(FileTypeEnum.EXCEL.getFileType(), excelSuffixName);
Set<String> htmlSuffixName = new HashSet<>();
htmlSuffixName.add("html");
fileTypeSuffixMap.put(FileTypeEnum.HTML.getFileType(), htmlSuffixName);
Set<String> mp3SuffixName = new HashSet<>();
mp3SuffixName.add("mp3");
fileTypeSuffixMap.put(FileTypeEnum.MP3.getFileType(), mp3SuffixName);
Set<String> lrcSuffixName = new HashSet<>();
lrcSuffixName.add("lrc");
fileTypeSuffixMap.put(FileTypeEnum.LRC.getFileType(), lrcSuffixName);
if (ObjUtil.isEmpty(fileTypeSuffixMap.get(fileType))) {
log.error("没有这样一个类型: {} 存放在类型map中", fileType);
return true;
}
return !(fileTypeSuffixMap.get(fileType).contains(imgSuffix));
}
}
初始化配置类
package com.zww.framework.config;
import com.google.gson.Gson;
import com.qiniu.common.Zone;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.zww.common.exception.GlobalException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QiniuConfig {
@Value("${qiniu.accessKey}")
private String accessKey;
@Value("${qiniu.secretKey}")
private String secretKey;
@Value("${qiniu.zone}")
private String zone;
/**
* 配置空间的存储区域
*/
@Bean
public com.qiniu.storage.Configuration qiNiuConfig() {
switch (zone) {
case "huadong":
return new com.qiniu.storage.Configuration(Zone.huadong());
case "huabei":
return new com.qiniu.storage.Configuration(Zone.huabei());
case "huanan":
return new com.qiniu.storage.Configuration(Zone.huanan());
case "beimei":
return new com.qiniu.storage.Configuration(Zone.beimei());
default:
throw new GlobalException("存储区域配置错误");
}
}
/**
* 构建一个七牛上传工具实例
*/
@Bean
public UploadManager uploadManager() {
return new UploadManager(qiNiuConfig());
}
/**
* 认证信息实例
*/
@Bean
public Auth auth() {
return Auth.create(accessKey, secretKey);
}
/**
* 构建七牛空间管理实例
*/
@Bean
public BucketManager bucketManager() {
return new BucketManager(auth(), qiNiuConfig());
}
@Bean
public Gson gson() {
return new Gson();
}
}
测试代码
将本地图片File 对象转为multipartFile 然后调用方法
package com.zww;
import com.qiniu.common.QiniuException;
import com.zww.service.FileService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@RunWith(SpringRunner.class)
@SpringBootTest
public class QiniuTest {
@Autowired
private FileService fileService;
public static MultipartFile convert(File file) throws IOException {
InputStream inputStream = new FileInputStream(file);
return new MockMultipartFile("file", file.getName(), "image/jpeg", inputStream);
}
@Test
public void testUpload() throws IOException {
File file = new File("电脑图片链接xxxxx");
MultipartFile multipartFile = convert(file);
String result = fileService.uploadImg(multipartFile);
System.out.println("访问地址: " + result);
}
@Test
public void testDelete() throws QiniuException {
String result = fileService.deleteImg("helloworld");
System.out.println(result);
}
}
使用File作为参数,不过得
控制台输出
当然你也可以上传其他文件类型例如视频,文本,压缩包,这里不过多赘述,原理大差不差,前端传好参数,后端参数好好填入就行,配置文件别写错