提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
大家在日常浏览器点击下载操作的时候,有没有考虑过这个下载是怎么实现的?为什么当我们点击对应的下载按钮或者下载链接的时候,浏览器能够下载对应的文件?今天就来深度进行剖析解读一下。
一、下载功能核心Java代码
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
// 设置 header 和 contentType
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
// 输出附件
IoUtil.write(response.getOutputStream(), false, content);
//将传入的字节数组转换为MultipartFile
MultipartFile multipartFile = new ByteMultipartFile(content, filename, MediaType.APPLICATION_OCTET_STREAM_VALUE);
String fileName = FileUploadUtils.uploadMinio(multipartFile);
}
二、代码解析
1.设置 Content-Disposition 响应头
代码如下(示例):
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
这行代码设置了 Content-Disposition 响应头,其值为 “attachment”,这告诉浏览器这个响应的内容应该被视为一个附件,而不是在浏览器内直接显示的内容。filename 参数指定了下载文件的名称。使用 URLEncoder.encode 是为了确保文件名中的特殊字符被正确编码,以便浏览器可以正确处理。
2.设置 Content-Type 响应头
代码如下(示例):
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
这行代码设置了 Content-Type 响应头,其值为 “application/octet-stream”,这是一个通用的二进制文件类型,表示返回的数据是二进制流。当浏览器接收到这种类型的响应时,它会询问用户是否要保存文件,而不是尝试显示它。
3.写入文件内容到响应输出流
IoUtil.write(response.getOutputStream(), false, content);
这行代码将字节数组 content 写入到 HTTP 响应的输出流中。IoUtil.write 方法(假设这是某个实用工具类的一部分)负责将字节数组写入到输出流。一旦这些字节被写入,它们就会被发送到客户端。
4.文件写入到本地磁盘
writeFileToDisk("C:\\Users\\20968\\Desktop\\risk-back", filename, content);
//写入到本地磁盘
public static void writeFileToDisk(String filePath, String filename, byte[] content) throws IOException {
// 确定文件存放路径
File file = new File(filePath, filename);
// 确保目录存在
File directory = file.getParentFile();
if (!directory.exists()) {
directory.mkdirs();
}
// 写入文件
try (FileOutputStream fos = new FileOutputStream(file)) {
IoUtil.write(fos, false, content);
}
}
5.文件写入到Mnio中
5.1 主方法
//将传入的字节数组转换为MultipartFile
MultipartFile multipartFile = new ByteMultipartFile(content, filename, MediaType.APPLICATION_OCTET_STREAM_VALUE);
String fileName = FileUploadUtils.uploadMinio(multipartFile);
5.2 实现MultipartFile 接口
ByteMultipartFile 类通过实现 MultipartFile 接口,重新实现了接口中定义的方法,以满足特定的需求,即从字节数组创建一个 MultipartFile 对象,因为Minio的api接口中需要的是MultipartFile 对象,
public class ByteMultipartFile implements MultipartFile {
private final byte[] bytes;
private final String name;
private final String contentType;
public ByteMultipartFile(byte[] bytes, String name, String contentType) {
this.bytes = bytes;
this.name = name;
this.contentType = contentType;
}
@Override
public String getName() {
return name;
}
@Override
public String getOriginalFilename() {
return name;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return bytes == null || bytes.length == 0;
}
@Override
public long getSize() {
return bytes.length;
}
@Override
public byte[] getBytes() throws IOException {
return bytes;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(bytes);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
// Implement if needed
}
}
5.3文件上传工具类
// 删减版本
/**
* 文件上传工具类
*
* @author ruoyi
*/
public class FileUploadUtils {
/**
* 默认大小 50M
*/
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
/**
* 默认的文件名最大长度 100
*/
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
/**
* 默认上传的地址
*/
private static String defaultBaseDir = RuoYiConfig.getProfile();
public static void setDefaultBaseDir(String defaultBaseDir) {
FileUploadUtils.defaultBaseDir = defaultBaseDir;
}
public static String getDefaultBaseDir() {
return defaultBaseDir;
}
/**
* Minio默认上传的地址
*/
private static String bucketName = MinioConfig.getBucketName();
public static String getBucketName() {
return bucketName;
}
@Value("${minio.url}")
private String URL;
@Value("${minio.bucketName}")
private String BUCKETNAME;
@Value("${minio.accessKey}")
private String ACCESSKEY;
@Value("${minio.secretKey}")
private String SECRETKEY;
/**
* 以默认BucketName配置上传到Minio服务器
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
public static final String uploadMinio(MultipartFile file) throws IOException {
try {
return uploadMinino(getBucketName(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
}
总结
当浏览器接收到带有这些特定头的响应时,它会识别出这是一个文件下载请求,并自动弹出一个对话框让用户选择保存文件的位置。这就是为什么即使方法返回值是 void,前端仍然可以下载文件的原因。实际上,文件的传输是在 HTTP 响应体中完成的,而不是通过方法的返回值。
至于 MultipartFile 的创建部分,这看起来像是在服务器端处理文件上传的逻辑,可能是为了将上传的文件存储到 MinIO 或其他存储服务中。这部分代码与前端下载逻辑无关,它只是将字节数组转换为一个 MultipartFile 对象,然后可能用于进一步的处理,例如上传到服务器或云存储。
标签:文件,return,String,Spring,filename,MultipartFile,public,下载 From: https://blog.csdn.net/weixin_51480428/article/details/141125426