02_使用 AES 算法实现文件加密上传至阿里云、解密下载
一、文件上传下载接口 controller 层
java
@RestController
@RequestMapping("/api/common/file")
@Api(tags = "公共文件上传")
@AllArgsConstructor
@Slf4j
public class FileV2Controller {
private final OssUtil ossUtil;
/**
* 上传单个加密文件并返回新文件名
* @param file
* @return
*/
@PostMapping("/uploadfile/encrypt")
@ApiOperation("上传单个加密文件")
public CommonResult<String> fileUploadEncrypt(@RequestParam("file") MultipartFile file) {
try {
//获取文件对应的字节数组
byte[] originalBytes = file.getBytes();
//这里是密钥
String secretKey = "1234567890123456"; // 记得换成你自己的密钥
//为文件生成一个新文件名 前面为UUID 后面为 文件后缀 (如原始文件名为 test.txt 加密后文件名 为 0x12.txt)
String newFileName = UUID.randomUUID() + getFileSuffix(file.getOriginalFilename());
//加密上传 原始文件字节数组 新文件名 密钥
ossUtil.uploadEncryptedFile(originalBytes, newFileName, secretKey);
//返回文件名
return CommonResult.success(newFileName);
} catch (Exception e) {
log.error("加密上传文件失败", e);
return CommonResult.failed("上传失败");
}
}
/**
*
* @param fileName
* @return
*/
@GetMapping("/downloadfile/decrypt")
@ApiOperation("下载单个加密文件")
public ResponseEntity<byte[]> fileDownloadDecrypt(@RequestParam("fileName") String fileName) {
try {
//这里是密钥
String secretKey = "1234567890123456"; // 记得换成你自己的密钥
//解密下载 文件名 密钥
byte[] decryptedBytes = ossUtil.downloadDecryptedFile(fileName ,secretKey);
// 设置响应头部,通知浏览器下载文件
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDisposition(ContentDisposition.attachment().filename(fileName).build());
// 返回字节流,直接下载
return new ResponseEntity<>(decryptedBytes, headers, HttpStatus.OK);
} catch (Exception e) {
log.error("解密下载文件失败", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 工具方法,提取后缀
private String getFileSuffix(String filename) {
return filename.substring(filename.lastIndexOf("."));
}
@GetMapping("/previewfile/decrypt")
@ApiOperation("预览单个加密文件")
public ResponseEntity<byte[]> filePreviewDecrypt(@RequestParam("fileName") String fileName) {
try {
// 这里是密钥 后续可以通过配置文件进行管理
String secretKey = "1234567890123456"; // 记得换成你自己的密钥
// 解密下载 文件名 密钥
byte[] decryptedBytes = ossUtil.downloadDecryptedFile(fileName, secretKey);
// 获取文件类型(可根据实际情况调整)
String fileExtension = getFileSuffix(fileName).toLowerCase();
// 设置响应头,根据文件类型进行调整
HttpHeaders headers = new HttpHeaders();
// 根据不同文件类型设置Content-Type
if (fileExtension.equals(".jpg") || fileExtension.equals(".jpeg") || fileExtension.equals(".png") || fileExtension.equals(".gif")) {
// 图片文件
headers.setContentType(MediaType.IMAGE_JPEG); // 也可以根据具体类型选择
} else if (fileExtension.equals(".pdf")) {
// PDF文件
headers.setContentType(MediaType.APPLICATION_PDF);
} else if (fileExtension.equals(".txt")) {
// 文本文件
headers.setContentType(MediaType.TEXT_PLAIN);
} else {
// 默认二进制流
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
}
// 设置文件名,确保浏览器使用传递的文件名
headers.setContentDisposition(ContentDisposition.inline().filename(fileName).build());
// 返回字节流,直接预览
return new ResponseEntity<>(decryptedBytes, headers, HttpStatus.OK);
} catch (Exception e) {
log.error("解密预览文件失败", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
二、AESUtil 工具类
/**
* AES加密解密工具类
*/
public class AESUtil {
// 定义使用的加密算法(这里是AES)
private static final String ALGORITHM = "AES";
/**
* AES加密
*
* @param data 原始数据(要加密的内容,比如文件内容的byte数组)
* @param secretKey 密钥(必须是16字节长度的字符串,比如 "1234567890abcdef")
* @return 加密后的数据(byte数组)
* @throws Exception 抛出任何加密异常
*/
public static byte[] encrypt(byte[] data, String secretKey) throws Exception {
// 1. 创建一个AES专用的密钥对象(把字符串密钥变成加密用的key)
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), ALGORITHM);
// 2. 创建Cipher对象,指定用AES算法
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 3. 初始化加密模式
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 4. 执行加密操作,并返回加密后的数据
return cipher.doFinal(data);
}
/**
* AES解密
*
* @param data 加密后的数据(byte数组)
* @param secretKey 密钥(加密和解密必须用同一个密钥)
* @return 解密后的原始数据(byte数组)
* @throws Exception 抛出任何解密异常
*/
public static byte[] decrypt(byte[] data, String secretKey) throws Exception {
// 1. 创建一个AES专用的密钥对象
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), ALGORITHM);
// 2. 创建Cipher对象,指定用AES算法
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 3. 初始化解密模式
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 4. 执行解密操作,并返回解密后的数据
return cipher.doFinal(data);
}
}
三、OSS 工具类
java
@Component
public class OssUtil {
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.bucketName}")
private String bucketName;
//文件存储目录
private String filedir = "xxx/";
// 上传文件(指定文件名)
private boolean uploadFile2OSS(InputStream instream, String fileName) {
boolean success = false;
try {
// 创建上传Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(instream.available());
objectMetadata.setCacheControl("no-cache");
objectMetadata.setHeader("Pragma", "no-cache");
objectMetadata.setContentDisposition("inline;filename=" + fileName);
// 创建 OSSClient 实例
OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
// 上传文件
PutObjectResult putResult = ossClient.putObject(bucketName, filedir + fileName, instream, objectMetadata);
// 如果上传没有异常,设置 success 为 true
success = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (instream != null) {
instream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return success;
}
/**
* 通过指定文件名从 OSS 下载文件
* @param fileName 文件名
* @return 文件的 InputStream
*/
public InputStream downloadFileFromOSS(String fileName) {
InputStream inputStream = null;
try {
// 创建 OSSClient 对象
OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
// 创建 GetObjectRequest 请求对象
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, filedir + fileName);
// 获取文件对象
OSSObject ossObject = ossClient.getObject(getObjectRequest);
// 获取文件的 InputStream
inputStream = ossObject.getObjectContent();
} catch (Exception e) {
e.printStackTrace();
}
return inputStream;
}
/**
* 上传加密后的文件
*
* @param bytes 文件原始内容(未加密)
* @param fileName 要保存的文件名
* @param secretKey 16位AES密钥
* @return 文件访问地址
*/
public Boolean uploadEncryptedFile(byte[] bytes, String fileName, String secretKey) {
try {
// 1. 加密 生成原始文件内容加密后的 字节数组
byte[] encryptedBytes = AESUtil.encrypt(bytes, secretKey);
// 2. 生成流 通过加密后字节数组 转为一个字节输入流
ByteArrayInputStream inputStream = new ByteArrayInputStream(encryptedBytes);
// 3. 上传 上传到oss
Boolean result = this.uploadFile2OSS(inputStream, fileName);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 下载并解密文件
*
* @param fileName 文件名
* @param secretKey 16位AES密钥
* @return 解密后的原始字节数组
*/
public byte[] downloadDecryptedFile(String fileName, String secretKey) {
try {
// 1. 获取文件输入流(加密后的内容)
InputStream encryptedInputStream = this.downloadFileFromOSS(fileName);
if (encryptedInputStream == null) {
return null;
}
// 2. 将输入流转为字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = encryptedInputStream.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
byte[] encryptedBytes = baos.toByteArray();
// 3. 解密
return AESUtil.decrypt(encryptedBytes, secretKey);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}