Skip to content

Commit ea3a1fb

Browse files
committed
feat(内核): 支持 Part 文件上传
1 parent 4cb9632 commit ea3a1fb

File tree

15 files changed

+279
-32
lines changed

15 files changed

+279
-32
lines changed

.github/workflows/publish-docs.yml

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,10 @@ jobs:
1717
uses: actions/setup-node@v3
1818
with:
1919
node-version: ${{ matrix.node-version }}
20-
21-
- name: Install pnpm
22-
uses: pnpm/action-setup@v2
23-
with:
24-
version: 8
25-
26-
- name: Get pnpm store directory
27-
shell: bash
28-
run: |
29-
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
30-
31-
- uses: actions/cache@v3
32-
name: Setup pnpm cache
33-
with:
34-
path: ${{ env.STORE_PATH }}
35-
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
36-
restore-keys: |
37-
${{ runner.os }}-pnpm-store-
20+
cache: 'npm'
3821

3922
- name: Install dependencies
40-
run: pnpm install pageforge -g
23+
run: npm install pageforge -g
4124

4225
- name: Build
4326
run: |

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ LightCall 的设计目标是提供一个简单、直观且功能强大的服务
3737
3. 可扩展性优先
3838
4. 开发体验至上
3939

40+
##
41+
4042
## 贡献指南
4143

4244
我们欢迎任何形式的贡献,包括但不限于:

docs/content/index.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: 欢迎使用 LightCall
3-
layout: home
3+
template: home
44

55
config:
66
sidebar: false
@@ -46,10 +46,10 @@ stats:
4646
title: 用数据说话
4747
description: 我们取得的成就
4848
items:
49-
- label: 活跃用户
50-
value: 1+
51-
- label: 服务客户
52-
value: 1+
49+
- label: Github Stars
50+
value: 5+
51+
- label: 版本发布
52+
value: 2
5353
- label: 正常运行时间
5454
value: 99.99%
5555
- label: 客户满意度

docs/content/usage/part.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
title: Part
3+
---
4+
5+
LightCall 提供了文件上传的支持。
6+
7+
!!! danger "注意"
8+
9+
LightCall 需要在参数上添加 `@Part` 注解来标识该参数是一个文件上传请求。并且添加 `@Part` 的类必须是一个接口。
10+
11+
!!!
12+
13+
我们使用的模拟数据是,他的代码可以在 [这里](https://github.com/devlive-community/lightcall/blob/dev/src/test/java/org/devlive/lightcall/example/part/PartService.java "PartService" "_blank") 查看。
14+
15+
```java
16+
public interface PartService
17+
{}
18+
```
19+
20+
### 用法
21+
22+
在参数上添加 `@Part` 注解,就可以实现文件上传请求了。
23+
24+
```java
25+
@Post("/upload")
26+
String uploadFile(@Part("file") File file);
27+
```
28+
29+
该示例中的 `uploadFile` 是一个文件上传请求,参数 `file` 表示要上传的文件,`"file"` 是文件参数的名称。请求路径是 `/upload`
30+
31+
### 文件类型
32+
33+
---
34+
35+
`@Part` 注解支持 `java.io.File` 类型的参数,用于上传本地文件。
36+
37+
```java
38+
@Post("/upload")
39+
String apply(@Part("image") File image);
40+
```
41+
42+
该示例中的 `apply` 方法用于上传图片文件,参数 `image` 表示要上传的图片文件,请求路径是 `/upload`
43+
44+
!!! info "提示"
45+
46+
文件上传请求会自动设置 `Content-Type` 为 `multipart/form-data`,并将文件作为表单的一部分上传。
47+
48+
!!!
49+
```

docs/pageforge.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ nav:
4141
- /usage/patch
4242
- /usage/options
4343
- /usage/head
44+
- /usage/part
4445
- 发布日志:
4546
- /release/latest

lightcall-core/src/main/java/org/devlive/lightcall/RequestContext.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,16 @@ public void setBody(Object body, MediaType mediaType)
5353
throw new IllegalArgumentException("MediaType cannot be null");
5454
}
5555
this.mediaType = mediaType;
56-
this.body = RequestBody.create(
57-
objectMapper.writeValueAsString(body).getBytes(StandardCharsets.UTF_8),
58-
mediaType
59-
);
56+
57+
if (body instanceof RequestBody) {
58+
this.body = (RequestBody) body;
59+
}
60+
else {
61+
this.body = RequestBody.create(
62+
objectMapper.writeValueAsString(body).getBytes(StandardCharsets.UTF_8),
63+
mediaType
64+
);
65+
}
6066
}
6167

6268
public void setMediaType(MediaType mediaType)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.devlive.lightcall.annotation;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
@Documented
10+
@Target(ElementType.PARAMETER)
11+
@Retention(RetentionPolicy.RUNTIME)
12+
public @interface Part
13+
{
14+
/**
15+
* 文件参数的名称
16+
*/
17+
String value();
18+
}

lightcall-core/src/main/java/org/devlive/lightcall/handler/ParameterHandlerFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static List<ParameterHandler> createHandlers(
1818
handlers.add(PathVariableHandler.create());
1919
handlers.add(HeaderHandler.create(context.getRequestBuilder(), method));
2020
handlers.add(BodyHandler.create(context));
21+
handlers.add(PartHandler.create(context));
2122
return handlers;
2223
}
2324
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package org.devlive.lightcall.handler;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import lombok.extern.slf4j.Slf4j;
5+
import okhttp3.MediaType;
6+
import okhttp3.MultipartBody;
7+
import okhttp3.RequestBody;
8+
import org.devlive.lightcall.RequestContext;
9+
import org.devlive.lightcall.RequestException;
10+
import org.devlive.lightcall.annotation.Part;
11+
12+
import java.io.File;
13+
import java.lang.reflect.Parameter;
14+
15+
@Slf4j
16+
public class PartHandler
17+
implements ParameterHandler
18+
{
19+
private final RequestContext context;
20+
21+
private PartHandler(RequestContext context)
22+
{
23+
this.context = context;
24+
}
25+
26+
public static PartHandler create(RequestContext context)
27+
{
28+
if (context == null) {
29+
throw new NullPointerException("RequestContext cannot be null");
30+
}
31+
32+
log.debug("Creating PartHandler");
33+
return new PartHandler(context);
34+
}
35+
36+
@Override
37+
public boolean canHandle(Parameter parameter)
38+
{
39+
return parameter.isAnnotationPresent(Part.class);
40+
}
41+
42+
@Override
43+
public String handle(Parameter parameter, Object arg, String path)
44+
{
45+
if (arg == null) {
46+
throw new IllegalArgumentException("Part file cannot be null");
47+
}
48+
49+
if (!(arg instanceof File)) {
50+
throw new IllegalArgumentException("Parameter annotated with @Part must be a File");
51+
}
52+
53+
Part partAnnotation = parameter.getAnnotation(Part.class);
54+
File file = (File) arg;
55+
56+
MultipartBody.Builder multipartBuilder = new MultipartBody.Builder()
57+
.setType(MultipartBody.FORM);
58+
59+
RequestBody fileBody = RequestBody.create(
60+
file,
61+
MediaType.parse("application/octet-stream")
62+
);
63+
64+
multipartBuilder.addFormDataPart(
65+
partAnnotation.value(),
66+
file.getName(),
67+
fileBody
68+
);
69+
70+
try {
71+
context.setBody(multipartBuilder.build(), MultipartBody.FORM);
72+
}
73+
catch (JsonProcessingException e) {
74+
throw new RequestException("Failed to set form part", e);
75+
}
76+
77+
log.debug("Added file part {}: {}", partAnnotation.value(), file.getName());
78+
79+
return path;
80+
}
81+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.devlive.lightcall.example.part;
2+
3+
import org.devlive.lightcall.annotation.Part;
4+
import org.devlive.lightcall.annotation.Post;
5+
6+
import java.io.File;
7+
8+
public interface PartService
9+
{
10+
@Post("/upload")
11+
String apply(@Part("file") File file);
12+
}

0 commit comments

Comments
 (0)