): 文档《响应实体类》。
---
# 概述
URL: https://tio-boot.com/zh/06_web/01.html
Source: zh/06_web/01.md
# 概述
## TestControler
在 Web 开发中,常用的类包括 HttpRequest, HttpResponse, Reps 等。以下是一个使用了 tio-boot controller 的 Java 控制器示例:
```java
package nexus.io.tio.web.hello.controller;
import nexus.io.annotation.AController;
import nexus.io.annotation.RequestPath;
@AController
@RequestPath("/test")
public class TestController {
@RequestPath
public String index() {
return "index";
}
}
```
这段代码演示了一些重要的注解的用法:
1. `@AController`: 这个注解标识一个类作为控制器(Controller)。如果一个类已经被 `@RequestPath` 注解标记,那么可以省略 `@AController` 注解。
2. `@RequestPath`: 这个注解用于指明控制器的请求路径。在这个例子中,它将 `TestController` 类关联到了 `/test` 路径。此外,当一个控制器类的成员方法返回值不为 void 时,tio-boot 会自动将这些方法添加到请求路由中。方法名将作为子路径。也可以通过在方法上使用 `@RequestPath` 来手动指定路由路径。
在 TIO HTTP Server 中,一个控制器(Controller)的方法(即 Action 方法)支持注入以下类型的参数:
- `ServerChannelContext`
- `HttpConfig`
- `HttpSession`
- `HttpRequest`
## IndexController
```
import nexus.io.annotation.RequestPath;
@RequestPath("/")
public class IndexController {
@RequestPath("")
public String index() {
return "uh-course";
}
}
```
1. **类声明和注解**:
- `@RequestPath("/")`: 这个注解将整个 `IndexController` 类映射到根路径 `/`。这意味着当请求的 URL 以 `/` 开头时,可能会由这个控制器处理。
2. **方法声明和注解**:
- `@RequestPath("")`: 这个注解将 `index` 方法映射到相对路径为空的请求,即根路径 `/`。当请求 URL 是 `/` 时,这个方法会被调用。
- `public String index()`: 这是一个公开的方法,它返回一个字符串 `"uh-course"`。这个字符串将作为 HTTP 响应的内容返回给客户端。
测试
```
>curl http://localhost/
uh-course
>curl http://localhost
uh-course
```
---
# 接收请求参数
URL: https://tio-boot.com/zh/06_web/02.html
Source: zh/06_web/02.md
# 接收请求参数
### get 获取参数
```
@RequestPath(value = "/get")
public HttpResponse get(String before, String end, HttpRequest request) throws Exception {
HttpResponse ret = Resps.html(request, "before:" + before + "
end:" + end);
return ret;
}
```
这段代码定义了一个名为 `get` 的方法,通过 `@RequestPath` 注解映射到 URL 路径 `/get`。方法接收两个字符串参数 `before` 和 `end`,以及一个 `HttpRequest` 对象 `request`。
### Post 获取参数
```
@RequestPath(value = "/post")
public HttpResponse post(String before, String end, User user, Short shortid, HttpRequest request) throws Exception {
HttpResponse ret = Resps.html(request, "before:" + before + "
end:" + end + "
user:" + Json.toFormatedJson(user) + "
");
return ret;
}
```
这段代码定义了一个名为 `post` 的方法,通过 `@RequestPath` 注解映射到 `/post` URL 路径。这个方法接收几个参数:两个字符串 `before` 和 `end`,一个 `User` 对象 `user`,一个 `Short` 类型的 `shortid`,以及一个 `HttpRequest` 对象 `request`。
这个方法的主要功能是接收 HTTP POST 请求,获取其中的参数和用户对象,并将这些信息以 HTML 格式的响应返回。
### 从请求地址中获取参数
示例代码
```
@RequestPath(value = "/var/{name}/{id}")
public HttpResponse var(String name, String id, HttpRequest request) throws Exception {
HttpResponse ret = Resps.json(request, "name:" + name + "\r\n" + "id:" + id);
return ret;
}
```
这段代码定义了一个处理 HTTP 请求的方法 `var`,该方法映射到一个 URL 路径模式 `/var/{name}/{id}`。这里的 `{name}` 和 `{id}` 是路径变量,它们在 URL 中动态替换成实际的值。方法接收两个字符串参数 `name` 和 `id`,这些参数自动从匹配的 URL 中提取。还有一个 `HttpRequest` 参数,代表接收到的 HTTP 请求。
方法的主体创建并返回一个 `HttpResponse` 对象,其中包含 `name` 和 `id` 的值。通过 `Resps.json` 方法生成响应,它将 `name` 和 `id` 的值格式化为 JSON 格式的字符串。这样,当访问对应的 URL 时,此方法将以 JSON 格式返回提取的 `name` 和 `id` 参数值。
---
# 接收日期参数
URL: https://tio-boot.com/zh/06_web/03.html
Source: zh/06_web/03.md
# 接收日期参数
在 Tio-Boot 框架中,处理日期类型参数是开发中的常见场景。通过框架内置的类型转换功能,可以轻松地接收和处理前端传递的日期数据
## Date
### 使用 Tio-Boot 框架处理 `Date` 类型参数
以下是一个简单的示例,演示如何在 Controller 中接收 `Date` 类型的参数。
#### 示例代码
```java
import com.jfinal.kit.Kv;
import nexus.io.annotation.RequestPath;
import nexus.io.tio.utils.resp.RespBodyVo;
@RequestPath("/base")
public class BaseController {
public RespBodyVo date(java.util.Date date) {
Kv kv = Kv.create();
kv.set("date", date);
return RespBodyVo.ok(kv);
}
}
```
在上面的代码中,`BaseController` 通过 `date()` 方法接收一个 `java.util.Date` 类型的参数。这个参数将由前端以特定格式传递,并自动转换为 `Date` 对象。
#### 请求示例
前端请求可以如下发送:
```
date=2014-09-04 08:34:00
```
此请求将会传递一个日期字符串,该字符串将被 Tio-Boot 框架自动解析为 `java.util.Date` 对象,并传递给 `date()` 方法。
#### 响应示例
服务端返回的响应数据可能如下所示:
```json
{
"data": {
"date": "2014-09-04 08:34:00"
},
"ok": true,
"msg": null,
"code": 1
}
```
在响应中,`data` 字段包含了请求传递的日期值。这个日期数据可以用于进一步的业务逻辑处理或直接返回给前端。
---
# 接收数组参数
URL: https://tio-boot.com/zh/06_web/04.html
Source: zh/06_web/04.md
# 接收数组参数
在 Web 应用程序中,处理包含数组参数的 HTTP 请求是一项常见的需求。本文将介绍两种处理数组参数的方法:
1. **使用框架自带的绑定功能(From)**
2. **使用自定义解析逻辑**
## 1. 使用框架自带的绑定功能(From)
利用框架提供的注解和自动绑定功能,可以简化数组参数的接收和处理。以下是一个示例方法,展示如何通过 `@RequestPath` 注解接收数组参数。
### 示例代码
```java
import nexus.io.annotation.RequestPath;
import nexus.io.http.HttpRequest;
import nexus.io.http.HttpResponse;
import nexus.io.http.Resps;
import nexus.io.json.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ArrayController {
private static final Logger log = LoggerFactory.getLogger(ArrayController.class);
@RequestPath(value = "/array")
public HttpResponse array(String[] names, Integer[] ids, int[] primitiveIds, HttpRequest request) throws Exception {
// 将数组参数转换为格式化的 JSON 字符串,并拼接作为响应内容
String jsonResponse = Json.toFormatedJson(names) + Json.toFormatedJson(ids) + Json.toFormatedJson(primitiveIds);
HttpResponse ret = Resps.json(request, jsonResponse);
// 从请求中获取名为 "names" 的参数数组
Object[] xx = request.getParamArray("names");
log.info("Received names array: {}", Json.toFormatedJson(xx));
return ret;
}
}
```
### 方法说明
- **方法签名**:
- `array` 方法通过 `@RequestPath` 注解映射到 URL 路径 `/array`。
- 接收四个参数:
- `String[] names`:字符串数组。
- `Integer[] ids`:包装类 `Integer` 数组。
- `int[] primitiveIds`:基本类型 `int` 数组。
- `HttpRequest request`:HTTP 请求对象,用于获取额外的请求信息。
- **方法逻辑**:
1. **生成响应内容**:
- 使用 `Json.toFormatedJson` 方法将 `names`、`ids` 和 `primitiveIds` 数组转换为格式化的 JSON 字符串。
- 将这些 JSON 字符串拼接起来,作为响应的内容。
- 通过 `Resps.json` 方法创建一个 `HttpResponse` 对象,包含上述 JSON 内容。
2. **获取并记录参数数组**:
- 使用 `request.getParamArray("names")` 方法从请求中获取名为 `"names"` 的参数数组,并存储在 `Object[] xx` 中。
- 使用日志记录工具(`log.info`)记录 `xx` 数组的格式化 JSON 表示,便于调试和监控。
3. **返回响应**:
- 返回包含 `names`、`ids` 和 `primitiveIds` 数组 JSON 表示的 `HttpResponse` 对象。
### HTML 表单示例
前端可以通过 HTML 表单发送数组参数。以下是一个示例表单,展示如何通过多个 `input` 标签传递数组数据。
```html
```
### 注意事项
- **参数名称一致性**:确保前端 `input` 标签的 `name` 属性与后端方法参数名称一致,以便框架能够正确绑定。
- **数据类型匹配**:前端传递的参数值应与后端接收的数组类型匹配,避免类型转换错误。
## 2. 使用自定义解析逻辑
有时,使用框架自带的绑定功能可能不够灵活或过于繁琐。此时,可以选择自定义解析请求参数,手动处理数组数据。
### 示例代码
```java
import nexus.io.annotation.RequestPath;
import nexus.io.model.body.RespBodyVo;
import nexus.io.tio.http.common.HttpRequest;
@RequestPath("/api/v1/email")
public class ApiV1EmailController {
/**
* 发送邀请邮件
*
* @param request HTTP 请求对象
* @return 响应体,包含处理结果
*/
public RespBodyVo sendInviteEmail(HttpRequest request) {
// 获取名为 "ids" 的参数
String ids = request.getParam("ids");
// 参数校验
if (ids == null || ids.trim().isEmpty()) {
return RespBodyVo.fail("ids 不能为空");
}
// 分割参数字符串,转换为 long 类型数组
String[] split = ids.split(",");
long[] studentIds = new long[split.length];
try {
for (int i = 0; i < split.length; i++) {
studentIds[i] = Long.parseLong(split[i].trim());
}
} catch (NumberFormatException e) {
return RespBodyVo.fail("ids 格式错误,必须为数字");
}
// 返回成功响应,包含转换后的 studentIds 数组
return RespBodyVo.ok(studentIds);
}
}
```
### 方法说明
- **方法签名**:
- `sendInviteEmail` 方法通过 `@RequestPath` 注解映射到 URL 路径 `/api/v1/email/sendInviteEmail`。
- 接收一个参数 `HttpRequest request`,用于获取请求中的参数。
- **方法逻辑**:
1. **获取参数**:
- 使用 `request.getParam("ids")` 方法获取请求中名为 `"ids"` 的参数值。
2. **参数校验**:
- 检查 `ids` 是否为 `null` 或空字符串,若是,则返回失败响应,提示 `"ids 不能为空"`。
3. **参数解析**:
- 使用 `split(",")` 方法将 `ids` 字符串按逗号分割,得到一个字符串数组。
- 创建一个 `long[]` 类型的数组 `studentIds`,用于存储转换后的数字。
- 遍历分割后的字符串数组,使用 `Long.parseLong` 方法将每个字符串转换为 `long` 类型,并存入 `studentIds` 数组。
- 如果转换过程中发生 `NumberFormatException` 异常,说明参数格式错误,返回失败响应,提示 `"ids 格式错误,必须为数字"`。
4. **返回响应**:
- 使用 `RespBodyVo.ok(studentIds)` 返回成功响应,包含转换后的 `studentIds` 数组。
### 前端请求示例
使用 `curl` 命令发送 POST 请求,传递 `ids` 参数。
```bash
curl --location --request POST 'http://localhost/api/v1/email/sendInviteEmail' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Authorization: Bearer PAfvyWa268y0on8MbxHhfYY21rvi9Sx8' \
--header 'Accept: */*' \
--header 'Host: localhost' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'ids=1,2,3,4,5,6,7,8,9'
```
### 后端响应示例
成功处理请求后,后端返回如下 JSON 响应:
```json
{
"data": [1, 2, 3, 4, 5, 6, 7, 8, 9],
"code": 1,
"msg": null,
"ok": true
}
```
### 注意事项
- **参数格式**:确保前端传递的 `ids` 参数为逗号分隔的数字字符串,例如 `"1,2,3,4,5,6,7,8,9"`。
- **异常处理**:在参数解析过程中,捕获可能的格式异常,及时返回有意义的错误信息,提升用户体验。
- **安全性**:对传入的参数进行必要的校验和清理,防止潜在的安全风险,如注入攻击。
## 总结
在 Web 应用程序中,处理数组参数有多种方法。使用框架自带的绑定功能可以简化开发,但在某些情况下,自定义解析逻辑提供了更高的灵活性和控制力。根据具体需求选择合适的方法,确保代码的可维护性和系统的稳定性。
---
# 返回字符串
URL: https://tio-boot.com/zh/06_web/05.html
Source: zh/06_web/05.md
# 返回字符串
```
package nexus.io.tio.boot.hello.AController;
import nexus.io.annotation.RequestPath;
@AController
@RequestPath("/test/string")
public class TestStringController {
@RequestPath()
public String index() {
return "index";
}
}
```
这段代码定义了一个使用 tio-boot 框架的 HTTP 控制器,专门用于处理 /test/string 路径下的 Web 请求,并返回一个字符串。
- `@RequestPath()`: 方法注解,指定了此方法处理的具体请求路径。结合类注解,当 HTTP 请求发送到 `/test/string` 时,此方法将被调用。
- 方法不接收任何参数,这意味着它响应的是不需要任何输入的请求。
- 方法返回一个字符串 `"index"`。在 tio-boot 框架中,会直接将字符串 `"index"` 作为响应内容发送给客户端不会查找名为 `"index"` 的视图模板,并用该模板生成 HTTP 响应。
---
# 返回文本数据
URL: https://tio-boot.com/zh/06_web/06.html
Source: zh/06_web/06.md
# 返回文本数据
```
@RequestPath(value = "/txt")
public HttpResponse txt(HttpRequest request) throws Exception {
HttpResponse ret = Resps.txt(request, txt);
return ret;
}
```
这段代码定义了一个名为 `txt` 的方法,通过 `@RequestPath` 注解映射到 `/txt` URL 路径。该方法接收一个 `HttpRequest` 对象 `request`。
方法执行以下操作:
- 使用 `Resps.txt` 方法创建一个 `HttpResponse` 对象。这个方法预计会将一个变量 `txt`(其定义未在代码段中显示)的内容作为文本格式的 HTTP 响应返回。
这个方法的主要功能是处理 HTTP 请求,并以纯文本格式返回 `txt` 变量中的内容。这种方式通常用于返回简单的文本数据。
---
# 返回网页
URL: https://tio-boot.com/zh/06_web/07.html
Source: zh/06_web/07.md
# 返回网页
## 概述
本文档介绍如何使用 tio-boot 框架创建 Controller 来直接返回网页内容。通过这种方式,可以将存储在数据库中的 HTML 内容直接渲染到浏览器中。
## 控制器实现
### 完整代码示例
```java
import nexus.io.annotation.RequestPath;
import nexus.io.db.activerecord.Db;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.tio.http.server.util.Resps;
@RequestPath("/amazon_products")
public class AmazonProductsHtmlController {
@RequestPath("/{id}")
public HttpResponse html(Long id) {
String html = Db.queryStr("select html from amazon_products_html where id=?", id);
HttpResponse response = TioRequestContext.getResponse();
Resps.html(response, html);
return response;
}
}
```
### 代码解析
#### 1. 注解说明
- `@RequestPath("/amazon_products")`: 类级别的注解,定义控制器的基础路径
- `@RequestPath("/{id}")`: 方法级别的注解,定义具体的路由路径,其中 `{id}` 是路径参数
#### 2. 核心组件
- **Db.queryStr()**: 从数据库中查询 HTML 内容
- 执行 SQL 查询:`select html from amazon_products_html where id=?`
- 参数化查询防止 SQL 注入
- **TioRequestContext.getResponse()**: 获取当前的 HTTP 响应对象
- **Resps.html()**: 设置响应内容为 HTML 格式
#### 3. 执行流程
1. 用户访问 `/amazon_products/{id}` 路径
2. 控制器接收 `id` 参数
3. 从数据库查询对应的 HTML 内容
4. 将查询结果设置为 HTTP 响应的 HTML 内容
5. 返回完整的 HTTP 响应
### 路由访问
```
GET /amazon_products/123
```
其中 `123` 是数据库记录的 ID。
### 数据库表结构
```sql
CREATE TABLE amazon_products_html (
id BIGINT PRIMARY KEY,
html TEXT
);
```
---
# 请求和响应字节
URL: https://tio-boot.com/zh/06_web/08.html
Source: zh/06_web/08.md
# 请求和响应字节
### 获取请求字节
```
package nexus.io.tio.boot.hello.AController;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.annotation.RequestPath;
@AController
@RequestPath("/test/bytes")
public class TestBytesController {
@RequestPath
public String index(HttpRequest reuqest) {
byte[] body = reuqest.getBody();
return "index";
}
}
```
**Method `index`**:
- `@RequestPath`: 方法注解,没有提供具体的路径值,这意味着它将使用类级别的路径 `/test/bytes`。因此,当 HTTP 请求发送到 `/test/bytes` 时,此方法将被调用。
- 方法接收一个 `HttpRequest` 对象作为参数,代表接收到的 HTTP 请求。
- `byte[] body = reuqest.getBody();`: 从 HTTP 请求中获取字节数组格式的请求体。
- 方法返回一个字符串 `"index"`。这个字符串会会作为 Response 返回。
这对深度学习框架很有用,可以它获取客户端发送的 NumPy 的字节数据
### 响应字节
直接响应
```
@RequestPath("/pen/raw/data")
@Slf4j
public class PenRawDataController {
@RequestPath()
public byte[] index() {
byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
HttpResponse response = TioControllerContext.getResponse();
response.setBody(bytes);
return bytes;
}
}
```
封装为 HttpResponse 响应
```
import nexus.io.tio.http.common.HttpResponse;
@RequestPath("/pen/raw/data")
@Slf4j
public class PenRawDataController {
@RequestPath()
public HttpResponse index() {
byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
HttpResponse response = TioControllerContext.getResponse();
response.setBody(bytes);
return response;
}
}
```
---
# 文件上传
URL: https://tio-boot.com/zh/06_web/09.html
Source: zh/06_web/09.md
# 文件上传
## 接收文件
接受文件在 Controller 的方法中添加 UploadFile 类型的参数,在发送 http 请求时,请求的 key 是 UploadFile 类型的参数名.通常是 file,下面的案例是 uploadFile
```java
package nexus.io.tio.web.hello.controller;
import java.io.File;
import nexus.io.annotation.RequestPath;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.model.upload.UploadFile;
import nexus.io.tio.utils.hutool.FileUtil;
@RequestPath(value = "/simple/upload")
public class SimleUploadController {
@RequestPath(value = "")
public HttpResponse index(UploadFile uploadFile){
if (uploadFile != null) {
byte[] fileData = uploadFile.getData();
File file = new File(uploadFile.getName());
FileUtil.writeBytes(fileData, file);
}
HttpResponse response = TioRequestContext.getResponse();
return response.setString("sucess");
}
}
```
这段代码定义了一个处理 HTTP 请求的方法 `index`。该方法通过 `@RequestPath` 注解映射到一个空的 URL 路径(即默认路径)。它接收两个参数:一个 `UploadFile` 对象 `uploadFile` 和一个 `HttpRequest` 对象 `request`。
- 方法首先检查 `uploadFile` 是否为非空,如果非空,表示有文件上传。
- 然后,从 `uploadFile` 对象中获取文件数据(字节数组)和文件名。
- 使用 `FileUtil.writeBytes` 方法,将文件数据写入到一个新创建的 `File` 对象中,该对象使用上传文件的名称。
- 最后,该方法返回一个 JSON 格式的响应,内容为字符串 "success"。
这个方法的主要功能是处理文件上传的请求,将上传的文件保存在服务器上,并返回一个表示操作成功的响应。
#### 6.3.2.接受文件和包含上传参数
InputType 是 form 中的一个参数,可以省略
InputType 是 from 中的一个参数,可以省略
```
@RequestPath(value = "")
public HttpResponse index(UploadFile uploadFile, String InputType, String outputType, HttpRequest request)
throws Exception {
if (uploadFile != null) {
byte[] fileData = uploadFile.getData();
File file = new File(uploadFile.getName());
FileUtil.writeBytes(fileData, file);
}
return Resps.json(request, "success");
}
```
这段代码定义了一个处理 HTTP 请求的方法 `index`,它处理带有文件上传的请求。该方法接收四个参数:`UploadFile` 对象 `uploadFile`,两个字符串 `InputType` 和 `outputType`,以及一个 `HttpRequest` 对象 `request`。
- 如果 `uploadFile` 不为空,方法读取上传文件的数据(字节),然后创建一个新的 `File` 对象,文件名与上传的文件名相同。
- 使用 `FileUtil.writeBytes` 方法,将上传文件的数据写入到新创建的文件中。
- 最后,方法返回一个 JSON 格式的响应,内容为字符串 "success"。
#### 6.3.3.UploadFile 常用方法
类 `org.tio.http.common.UploadFile` 可能代表在 Web 应用程序上下文中的一个上传文件。下面是这些方法的简要说明:
1. `getName()`: 这个方法返回上传文件的名称。它用于识别文件,可能基于文件名扩展名确定其类型。
2. `getSize()`: 这个方法返回上传文件的大小,以字节为单位。它可以用来检查文件的大小,这对于验证目的(例如,确保文件不太大,应用程序能够处理)是必要的。
3. `getData()`: 这个方法返回文件的实际数据,以字节数组的形式。它用于处理文件的内容,如将其保存到服务器上或执行任何特定于文件的操作。
## 返回文件
```
@RequestPath(value = "/filetest")
public HttpResponse filetest(HttpRequest request) throws Exception {
HttpResponse ret = Resps.file(request, new File("d:/tio.exe"));
return ret;
}
@RequestPath(value = "/test.zip")
public HttpResponse test_zip(HttpRequest request) throws Exception {
String root = request.httpConfig.getPageRoot(request);
HttpResponse ret = Resps.file(request, new File(root, "test/test.zip"));
return ret;
}
```
这两个方法都涉及处理 HTTP 请求并返回文件作为响应。
1. `filetest` 方法:
- 通过 `@RequestPath` 注解映射到 `/filetest` URL 路径。
- 方法使用 `Resps.file` 创建一个 `HttpResponse` 对象,该对象将发送位于 "d:/tio.exe" 的文件作为响应。
2. `test_zip` 方法:
- 映射到 `/test.zip` URL 路径。
- 首先,使用 `request.httpConfig.getPageRoot` 获取服务器的根目录。
- 然后,使用 `Resps.file` 创建 `HttpResponse` 对象,返回位于根目录下 "test/test.zip" 路径的文件。
---
# 文件下载
URL: https://tio-boot.com/zh/06_web/10.html
Source: zh/06_web/10.md
# 文件下载
本文详细介绍了如何在 tio-boot 后端应用程序中实现文件下载功能,涵盖任意文件下载、浏览器触发下载、以及 DOCX/PDF 文件下载等多种场景,同时还展示了如何生成二维码并通过浏览器下载二维码图片。内容包括完整的代码示例和详细的原理解析,适用于希望扩展或自定义文件下载功能的开发者。
---
[[toc]]
## 1. 任意文件下载
在一些场景下,我们可能需要实现一个通用的文件下载接口,该接口能够根据传入的参数动态读取文件并返回给客户端。下面的示例代码展示了如何读取指定文件,并将文件字节流以合适的 MIME 类型返回给浏览器。
```java
package nexus.io.test.controller;
import java.io.File;
import nexus.io.annotation.RequestPath;
import nexus.io.media.NativeMedia;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.model.upload.UploadFile;
import nexus.io.tio.http.server.util.Resps;
import nexus.io.tio.utils.http.ContentTypeUtils;
import nexus.io.tio.utils.hutool.FileUtil;
import nexus.io.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 响应中设置特定的响应头,提示浏览器将内容作为附件下载。
### 示例代码
```java
package nexus.io.linux.handler;
import java.io.File;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.tio.utils.http.ContentTypeUtils;
import nexus.io.tio.utils.hutool.FileUtil;
import nexus.io.tio.utils.hutool.FilenameUtils;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DownloadHandler {
public HttpResponse donwload(HttpRequest httpRequest) {
HttpResponse httpResponse = TioRequestContext.getResponse();
String downloadFilename = "readme.md";
File file = new File(downloadFilename);
String suffix = FilenameUtils.getSuffix(downloadFilename);
// 设置响应内容类型,此处使用 Markdown 格式
String contentType = ContentTypeUtils.getContentType(suffix);
log.info("filename:{},{}", downloadFilename, contentType);
// 设置 Content-Type 响应头,确保浏览器以正确格式解析文件内容
httpResponse.setContentType(contentType);
// 设置 Content-Disposition 响应头,告知浏览器将内容作为附件下载,并指定默认文件名
httpResponse.setAttachmentFilename(downloadFilename);
// 设置响应体内容,实际项目中可替换为文件的字节流
httpResponse.setBody(FileUtil.readBytes(file));
return httpResponse;
}
}
```
### 原理说明
- **Content-Disposition**:设置为 `attachment` 告知浏览器将文件作为附件下载,同时 `filename` 参数指定了默认的文件名称。
- **Content-Type**:确保返回的内容格式与文件实际类型相匹配,从而使下载后的文件能够被正确识别。
http响应头
```
content-disposition:attachment; filename="readme.md"
content-type:application/octet-stream
```
---
## 3. 文档下载
针对常见的文档文件(如 DOCX 和 PDF),可以根据文件 ID 动态获取文件内容,并返回给客户端。以下分别介绍 DOCX 和 PDF 文件下载的实现方法。
### 3.1 下载 DOCX 文件
#### 示例代码
```java
@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 文件
#### 示例代码
```java
@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 依赖示例
```xml
com.google.zxing
core
3.4.1
com.google.zxing
javase
3.4.1
nexus.io
tio-boot
最新版本
```
### QrController 实现
#### 示例代码
```java
package nexus.io.test.controller;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import nexus.io.annotation.AController;
import nexus.io.annotation.RequestPath;
import nexus.io.model.body.RespBodyVo;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.tio.http.server.util.Resps;
import nexus.io.tio.utils.hutool.StrUtil;
import nexus.io.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 应用中实现文件下载功能的关键步骤:
1. **定义请求路径**:通过注解或路由配置定义对应的 URL 接口。
2. **读取文件**:根据实际需求读取服务器中的文件,支持任意文件、DOCX、PDF、二维码图片等。
3. **设置响应头**:重点在于设置 `Content-Type` 和 `Content-Disposition` 响应头,确保浏览器能够正确识别文件类型并触发下载。
4. **返回响应内容**:将读取的字节流写入 HTTP 响应体中,完成文件传输。
5. **错误处理**:合理捕获异常并返回友好的错误提示,提升接口的健壮性和用户体验。
通过正确的设置和处理,开发者可以在项目中灵活扩展文件下载功能,同时确保安全性和性能。希望本文能为您提供清晰的实现思路和实践参考。
---
# 返回视频文件并支持断点续传
URL: https://tio-boot.com/zh/06_web/11.html
Source: zh/06_web/11.md
# 返回视频文件并支持断点续传
本文档介绍如何通过 TioBoot Handler 返回视频文件,支持 HTTP Range 断点续传,以便前端页面使用 `