02_使用 AES 算法实现文件加密上传至阿里云、解密下载

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;
        }
    }
}
相关推荐
小黄人202520 分钟前
从零认识阿里云OSS:云原生对象存储的核心价值
阿里云·云原生·云计算
小梦白26 分钟前
RPG7.准备GAS的工作
java·开发语言
武昌库里写JAVA28 分钟前
【iview】icon样式
java·开发语言·spring boot·学习·课程设计
不太可爱的叶某人31 分钟前
【学习笔记】深入理解Java虚拟机学习笔记——第1章 走进Java
java·jvm·笔记·学习
颇有几分姿色1 小时前
Spring Boot 实现多种来源的 Zip 多层目录打包下载(本地文件&HTTP混合)
java·spring boot·后端
百锦再1 小时前
Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别
android·java·图像处理·opencv·kotlin·app·android studio
-XWB-1 小时前
【Java】打印运行环境中某个类引用的jar版本路径
java·开发语言
Brookty1 小时前
【Java学习】通配符?
java·学习
海码0072 小时前
【Hot 100】94. 二叉树的中序遍历
数据结构·c++·算法·二叉树·hot100
fhgfyrsg2 小时前
【无标题】
java