02_使用 AES 算法实现文件加密上传至阿里云、解密下载
02_使用 AES 算法实现文件加密上传至阿里云、解密下载
一、文件上传下载接口 controller 层
@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-Typeif (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 工具类
@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的MetadataObjectMetadata 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 为 truesuccess = 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);// 获取文件的 InputStreaminputStream = 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. 上传 上传到ossBoolean 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;}}
}