文件下载
本文详细介绍了如何在 tio-boot 后端应用程序中实现文件下载功能,涵盖任意文件下载、浏览器触发下载、以及 DOCX/PDF 文件下载等多种场景,同时还展示了如何生成二维码并通过浏览器下载二维码图片。内容包括完整的代码示例和详细的原理解析,适用于希望扩展或自定义文件下载功能的开发者。
1. 任意文件下载
在一些场景下,我们可能需要实现一个通用的文件下载接口,该接口能够根据传入的参数动态读取文件并返回给客户端。下面的示例代码展示了如何读取指定文件,并将文件字节流以合适的 MIME 类型返回给浏览器。
package com.litongjava.test.controller;
import java.io.File;
import com.litongjava.annotation.RequestPath;
import com.litongjava.media.NativeMedia;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.UploadFile;
import com.litongjava.tio.http.server.util.Resps;
import com.litongjava.tio.utils.http.ContentTypeUtils;
import com.litongjava.tio.utils.hutool.FileUtil;
import com.litongjava.tio.utils.hutool.FilenameUtils;
@RequestPath("/media")
public class MediaController {
public HttpResponse toMp3(HttpRequest request) {
// 指定要下载的文件名称(这里以 mp3 文件为例)
String result = "a.mp3";
// 根据文件后缀获取正确的 MIME 类型
String contentType = ContentTypeUtils.getContentType(FilenameUtils.getSuffix(result));
// 将文件读取为字节数组
byte[] fileBytes = FileUtil.readBytes(new File(result));
HttpResponse response = TioRequestContext.getResponse();
// 使用工具类将字节流和 MIME 类型设置到响应中
Resps.bytesWithContentType(response, fileBytes, contentType);
return response;
}
}
原理说明
- 文件读取:使用
FileUtil.readBytes()
将文件转换为字节数组,适用于文件较小的场景。 - ContentType:通过
ContentTypeUtils
根据文件后缀设置 MIME 类型,确保浏览器正确解析文件内容。 - 响应构建:利用
Resps.bytesWithContentType
工具类,封装文件字节流和 MIME 类型,返回一个 HTTP 响应。
2. 通过浏览器触发文件下载
在大多数 Web 应用中,我们希望通过浏览器的下载功能直接下载文件。为此,需要在 HTTP 响应中设置特定的响应头,提示浏览器将内容作为附件下载。
示例代码
package com.litongjava.tio.web.hello.controller;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DocumentController {
public HttpResponse export(Long documentId) {
String downloadFilename = "my.md";
log.info("filename:{}", downloadFilename);
// 设置响应内容类型,此处使用 Markdown 格式
String contentType = "text/markdown; charset=utf-8";
HttpResponse httpResponse = TioRequestContext.getResponse();
// 设置 Content-Type 响应头,确保浏览器以正确格式解析文件内容
httpResponse.setContentType(contentType);
// 设置 Content-Disposition 响应头,告知浏览器将内容作为附件下载,并指定默认文件名
httpResponse.setHeader("Content-Disposition", "attachment; filename=\"" + downloadFilename + "\"");
// 设置响应体内容,实际项目中可替换为文件的字节流
httpResponse.setString("Hi");
return httpResponse;
}
}
原理说明
- Content-Disposition:设置为
attachment
告知浏览器将文件作为附件下载,同时filename
参数指定了默认的文件名称。 - Content-Type:确保返回的内容格式与文件实际类型相匹配,从而使下载后的文件能够被正确识别。
3. 文档下载
针对常见的文档文件(如 DOCX 和 PDF),可以根据文件 ID 动态获取文件内容,并返回给客户端。以下分别介绍 DOCX 和 PDF 文件下载的实现方法。
3.1 下载 DOCX 文件
示例代码
@RequestPath("/download/docx/{id}")
public HttpResponse download(String id, HttpRequest request) {
// 根据传入的 id 动态获取 DOCX 文件的输入流
InputStream inputStream = ResourceUtil.getStream(id+".docx");
int available;
try {
available = inputStream.available();
byte[] fileBytes = new byte[available];
// 将文件内容读取到字节数组中
inputStream.read(fileBytes, 0, available);
// 设置 DOCX 文件的 MIME 类型
String contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=utf-8";
HttpResponse response = Resps.bytesWithContentType(request, fileBytes, contentType);
return response;
} catch (IOException e) {
e.printStackTrace();
// 文件读取异常时返回错误 JSON 响应
return Resps.json(request, RespBodyVo.fail("Error generating captcha"));
}
}
3.2 下载 PDF 文件
示例代码
@RequestPath("/download/pdf/{id}")
public HttpResponse downloadPdf(String id, HttpRequest request) {
log.info("id:{}", id);
// 从资源路径中动态读取 PDF 文件(假设文件存放在 pdf 目录下)
InputStream inputStream = ResourceUtil.getStream("pdf/" + id + ".pdf");
int available;
try {
available = inputStream.available();
byte[] fileBytes = new byte[available];
// 将文件内容读取到字节数组中
inputStream.read(fileBytes, 0, available);
// 设置 PDF 文件的 MIME 类型
HttpResponse response = Resps.bytesWithContentType(request, fileBytes, "application/pdf; charset=utf-8");
return response;
} catch (IOException e) {
e.printStackTrace();
// 文件读取异常时返回错误 JSON 响应
return Resps.json(request, RespBodyVo.fail("Error generating captcha"));
}
}
原理说明
- 资源获取:利用
ResourceUtil.getStream()
方法根据文件名或路径获取文件的输入流,适用于存储在项目资源中的文件。 - 字节流读取:调用
inputStream.available()
获取可读取的字节数,并一次性读取文件内容。对于大文件,应考虑使用流式传输以避免内存压力。 - 错误处理:在读取过程中捕获异常,并返回格式化的错误响应,确保接口的健壮性。
4. 生成二维码并下载
除了常规的文件下载外,有时还需要动态生成图片(例如二维码)并返回给客户端。下面以二维码生成为例,展示如何使用 ZXing 库生成二维码图片,并通过 HTTP 响应返回。
前置准备
请确保项目中已添加以下依赖:
Maven 依赖示例
<dependencies>
<!-- ZXing 核心库(用于二维码生成) -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
</dependency>
<!-- Tio-Boot 框架 -->
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>tio-boot</artifactId>
<version>最新版本</version>
</dependency>
</dependencies>
QrController 实现
示例代码
package com.litongjava.test.controller;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.litongjava.annotation.AController;
import com.litongjava.annotation.RequestPath;
import com.litongjava.model.body.RespBodyVo;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.util.Resps;
import com.litongjava.tio.utils.hutool.StrUtil;
import com.litongjava.tio.utils.qrcode.QrCodeUtils;
@AController
@RequestPath("/qr")
public class QrController {
@RequestPath("/gen")
public HttpResponse qr(String content) {
HttpResponse response = TioRequestContext.getResponse();
// 验证二维码内容不能为空
if (StrUtil.isBlank(content)) {
return Resps.json(response, RespBodyVo.fail("No content provided for QR code"));
}
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// 生成二维码图片,并写入到输出流中,二维码尺寸为 300x300 像素
QrCodeUtils.generateQRCode(content, 300, 300, outputStream);
// 将输出流转换为字节数组
byte[] qrCodeBytes = outputStream.toByteArray();
// 使用工具类返回包含二维码图片字节流的响应,设置 MIME 类型为 image/png
return Resps.bytesWithContentType(response, qrCodeBytes, "image/png");
} catch (IOException e) {
e.printStackTrace();
return Resps.json(response, RespBodyVo.fail("Error generating QR code"));
}
}
}
原理说明
- 二维码生成:利用 ZXing 库生成二维码图片,通过
QrCodeUtils.generateQRCode()
方法将二维码内容写入输出流中。 - 流转换:将
ByteArrayOutputStream
中的内容转换为字节数组,方便后续通过 HTTP 响应返回给客户端。 - 响应设置:设置正确的 MIME 类型
"image/png"
,确保浏览器能够识别并显示图片内容。
5. 总结
通过以上示例,我们可以看到在 Java Web 应用中实现文件下载功能的关键步骤:
- 定义请求路径:通过注解或路由配置定义对应的 URL 接口。
- 读取文件:根据实际需求读取服务器中的文件,支持任意文件、DOCX、PDF、二维码图片等。
- 设置响应头:重点在于设置
Content-Type
和Content-Disposition
响应头,确保浏览器能够正确识别文件类型并触发下载。 - 返回响应内容:将读取的字节流写入 HTTP 响应体中,完成文件传输。
- 错误处理:合理捕获异常并返回友好的错误提示,提升接口的健壮性和用户体验。
通过正确的设置和处理,开发者可以在项目中灵活扩展文件下载功能,同时确保安全性和性能。希望本文能为您提供清晰的实现思路和实践参考。