This is an automated email from the ASF dual-hosted git repository.
liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-docs.git
The following commit(s) were added to refs/heads/master by this push:
new 82ec48d update upload and download (#323)
82ec48d is described below
commit 82ec48d647df820a80f9d5a5c09804af2443ad86
Author: liubao68 <[email protected]>
AuthorDate: Sun Feb 18 15:34:49 2024 +0800
update upload and download (#323)
---
.../zh_CN/docs/build-provider/http-mapping.md | 4 +-
.../zh_CN/docs/general-development/CORS.md | 48 ++++++++++---
.../docs/general-development/file-download.md | 75 +++++++++++++------
.../zh_CN/docs/general-development/file-upload.md | 84 +++++++++++++---------
.../docs/general-development/upload-download.md | 3 +
java-chassis-reference/zh_CN/mkdocs.yml | 3 +-
6 files changed, 148 insertions(+), 69 deletions(-)
diff --git a/java-chassis-reference/zh_CN/docs/build-provider/http-mapping.md
b/java-chassis-reference/zh_CN/docs/build-provider/http-mapping.md
index 4841db4..94432a9 100644
--- a/java-chassis-reference/zh_CN/docs/build-provider/http-mapping.md
+++ b/java-chassis-reference/zh_CN/docs/build-provider/http-mapping.md
@@ -263,7 +263,9 @@ contents of multiPartParamExample2
----boundary-example----
```
->>> multipart/form-data 也可以表示 Form 参数, 和 application/x-www-form-urlencoded
用法一样。为了避免混淆和简洁,建议multipart/form-data用于文件上传场景。
+>>> multipart/form-data 也可以表示 Form 参数, 和 application/x-www-form-urlencoded
用法一样。为了避免混淆和简洁,建议multipart/form-data专用于文件上传场景。
+
+* Spring MVC
```java
@PostMapping(path = "multiPartSpringMVCExample", consumes =
MediaType.MULTIPART_FORM_DATA_VALUE)
diff --git a/java-chassis-reference/zh_CN/docs/general-development/CORS.md
b/java-chassis-reference/zh_CN/docs/general-development/CORS.md
index d3d95ce..ba6efa1 100644
--- a/java-chassis-reference/zh_CN/docs/general-development/CORS.md
+++ b/java-chassis-reference/zh_CN/docs/general-development/CORS.md
@@ -1,8 +1,8 @@
-# 跨域资源共享(CORS)配置
+# 跨域资源共享(CORS)
## 概念阐述
-跨域资源共享(CORS, Cross-Origin Resource Sharing)允许Web服务器进行跨域访问控制,使浏览器可以更安全地进行跨域数据传输。
+跨域资源共享(CORS, Cross-Origin Resource Sharing)允许浏览器安全的进行跨域资源访问。
## 场景描述
@@ -12,19 +12,21 @@
CORS功能在microservice.yaml文件中配置,配置项见下表所述。
-| 配置项 | 默认值 | 取值范围 | 是否必选 | 含义 | 注意 |
-| :--- | :--- | :--- | :--- | :--- | :--- |
-| servicecomb.cors.enabled | `false` | `true`/`false` | 否 | 是否开启CORS功能 | - |
-| servicecomb.cors.origin | `*` | - | 否 | Access-Control-Allow-Origin | - |
-| servicecomb.cors.allowCredentials | `false` | `true`/`false` | 否 |
Access-Control-Allow-Credentials |
根据CORS标准,当Access-Control-Allow-Credentials设置为`true`时,Access-Control-Allow-Origin不可设置为"*",否则将会抛出异常
|
-| servicecomb.cors.allowedHeader | 无 | - | 否 | Access-Control-Allow-Headers |
多个值使用逗号分隔 |
-| servicecomb.cors.allowedMethod | 无 | - | 否 | Access-Control-Allow-Methods |
多个值使用逗号分隔 |
-| servicecomb.cors.exposedHeader | 无 | - | 否 | Access-Control-Expose-Headers |
多个值使用逗号分隔 |
-| servicecomb.cors.maxAge | 无 | (0,2147483647],整型 | 否 | Access-Control-Max-Age
| 单位是秒,如果用户不配置此项,则CORS应答中没有Access-Control-Max-Age |
+| 配置项 | 默认值 | 取值范围 | 是否必选 | 含义
| 注意
|
+|:----------------------------------|:--------|:------------------|:-----|:---------------------------------|:--------------------------------------------------------------------------------------------------|
+| servicecomb.cors.enabled | `false` | `true`/`false` | 否 |
是否开启CORS功能 | -
|
+| servicecomb.cors.origin | `*` | - | 否 |
Access-Control-Allow-Origin | 多个值使用逗号分隔
|
+| servicecomb.cors.allowCredentials | `false` | `true`/`false` | 否 |
Access-Control-Allow-Credentials |
根据CORS标准,当Access-Control-Allow-Credentials设置为`true`时,Access-Control-Allow-Origin不可设置为"*",否则将会抛出异常
|
+| servicecomb.cors.allowedHeader | 无 | - | 否 |
Access-Control-Allow-Headers | 多个值使用逗号分隔
|
+| servicecomb.cors.allowedMethod | 无 | - | 否 |
Access-Control-Allow-Methods | 多个值使用逗号分隔
|
+| servicecomb.cors.exposedHeader | 无 | - | 否 |
Access-Control-Expose-Headers | 多个值使用逗号分隔
|
+| servicecomb.cors.maxAge | 无 | (0,2147483647],整型 | 否 |
Access-Control-Max-Age |
单位是秒,如果用户不配置此项,则CORS应答中没有Access-Control-Max-Age
|
## 示例代码
+允许所有:
+
```yaml
servicecomb:
cors:
@@ -34,3 +36,27 @@ servicecomb:
allowedMethod: PUT,DELETE
maxAge: 3600
```
+
+允许单个origin:
+
+```yaml
+servicecomb:
+ cors:
+ enabled: true
+ origin: "http://test.domain:8080"
+ allowCredentials: false
+ allowedMethod: PUT,DELETE
+ maxAge: 3600
+```
+
+配置多个origin:
+
+```yaml
+servicecomb:
+ cors:
+ enabled: true
+ origin: "http://test.domain:8080,http://test.domain:9090"
+ allowCredentials: false
+ allowedMethod: PUT,DELETE
+ maxAge: 3600
+```
diff --git
a/java-chassis-reference/zh_CN/docs/general-development/file-download.md
b/java-chassis-reference/zh_CN/docs/general-development/file-download.md
index d579f84..6406e0a 100644
--- a/java-chassis-reference/zh_CN/docs/general-development/file-download.md
+++ b/java-chassis-reference/zh_CN/docs/general-development/file-download.md
@@ -1,31 +1,63 @@
# 文件下载开发指导
-## 服务提供者开发
+Java Chassis提供了通用的 `文件下载` 支持。 如果返回值类型为 `File`、`Resource`、`InputStream`、`Part`
等,则被认为是 `文件下载`。 文件MIME类型和文件名可以使用 `Part` 的 API 指定。
-文件下载支持采用 Spring MVC 和 Jax RS 开发。 因为文件下载都是 GET 方法, 因此两者的使用差异很小, 这里的例子只提供
-Spring MVC 。
+* Spring MVC
-* File
+```java
+@GetMapping(path = "/downloadSpringMVCExample")
+public Part downloadSpringMVCExample(String content) throws IOException {
+ File file = createTempFile(content);
+ return new FilePart(null, file)
+ .setDeleteAfterFinished(true)
+ .setSubmittedFileName("test.bin")
+ .contentType("application/octet-stream");
+}
+```
-最简单的例子,接口的返回参数声明为 File 类型的参数, 即可定义一个下载接口。
+上述接口会返回如下响应头:
+```text
+Content-Disposition: attachment;filename=test.bin;filename*=utf-8’’test.bin
+Content-Encoding: gzip
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
```
-@GetMapping(path = "/file")
-public File file(String name)
+
+* JAX-RS
+
+```java
+@GET
+@Path("/downloadSpringMVCExample")
+public Part downloadSpringMVCExample(String content) throws IOException {
+ File file = createTempFile(content);
+ return new FilePart(null, file)
+ .setDeleteAfterFinished(true)
+ .setSubmittedFileName("test.bin")
+ .contentType("application/octet-stream");
+}
+```
+
+上述接口会返回如下响应头:
+
+```text
+Content-Disposition: attachment;filename=test.bin;filename*=utf-8’’test.bin
+Content-Encoding: gzip
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
```
-* Part
+## 服务提供者开发
+
+文件下载服务端定义建议使用 `Part`, 它提供了最丰富的功能,包括指定文件名、文件删除策略以及指定Content-Type等。 Java
Chassis也提供了下面的一些类型支持。
-如果需要根据请求参数动态创建临时文件,下载完成后,将临时文件删除,可以采用 Part 类型的参数。
+* File
+
+最简单的例子,接口的返回参数声明为 File 类型的参数, 即可定义一个下载接口。
```
@GetMapping(path = "/file")
-public Part file(String content) throws IOException {
-File file = createTempFile(content);
-return new FilePart(null, file)
- .setDeleteAfterFinished(true)
- .setSubmittedFileName("test.txt");
-}
+public File file(String name)
```
* Resource
@@ -50,8 +82,7 @@ public Resource resource() {
}
```
-上例中,因为ByteArrayResource没有文件名的概念,所以需要实现Resource的getFilename方法,也可以通过ResponseEntity
-进行包装:
+上例中,因为ByteArrayResource没有文件名的概念,所以需要实现Resource的getFilename方法,也可以通过ResponseEntity进行包装:
```
@GetMapping(path = "/resource")
@@ -89,9 +120,9 @@ public ResponseEntity<InputStream> download() throws
IOException {
* 文件类型判定
-只要没有通过ResponseEntity直接设置HttpHeaders.CONTENT\_TYPE,ServiceComb都会尝试通过File、Part、Resource中的文件名后缀进行自动判定。
+只要没有通过ResponseEntity直接设置HttpHeaders.CONTENT\_TYPE,Java
Chassis都会尝试通过File、Part、Resource中的文件名后缀进行自动判定。
-ServiceComb使用java的mime
type机制进行文件类型判定,如果业务场景中的文件后缀无法被识别,ServiceComb会默认处理为application/octet-stream
+Java Chassis使用java的mime type机制进行文件类型判定,如果业务场景中的文件后缀无法被识别,Java
Chassis会默认处理为application/octet-stream
如果这不满足要求,假设文件后缀为xyz,期望文件类型为application/file-xyz,以下方式任选一种均可解决:
@@ -136,18 +167,16 @@ public ResponseEntity<Part> tempFileEntity(String
content) throws IOException {
* 指定文件名
-只要没有通过ResponseEntity直接设置HttpHeaders.CONTENT\_DISPOSITION,ServiceComb都会尝试通过File、Part、Resource中的文件名生成HttpHeaders.CONTENT\_DISPOSITION,假设文件名为file.txt,则生成的数据如下:
+只要没有通过ResponseEntity直接设置HttpHeaders.CONTENT\_DISPOSITION,Java
Chassis都会尝试通过File、Part、Resource中的文件名生成HttpHeaders.CONTENT\_DISPOSITION,假设文件名为file.txt,则生成的数据如下:
```
Content-Disposition: attachment;filename=file.txt;filename*=utf-8’’file.txt
```
-不仅仅生成filename,还生成了filename\*,这是因为如果文件名中出现了中文、空格,并且filename正确地做了encode,ie、chrome都没有问题,但是firefox直接将encode后的串当作文件名直接使用了。firefox按照[https://tools.ietf.org/html/rtf6266](https://tools.ietf.org/html/rtf6266),只对filename\*进行解码。
+不仅仅生成 `filename` ,还生成了 `filename\*`
,这是因为如果文件名中出现了中文、空格,并且filename正确地做了encode,ie、chrome都没有问题,但是firefox直接将encode后的串当作文件名直接使用了。firefox按照[https://tools.ietf.org/html/rtf6266](https://tools.ietf.org/html/rtf6266)
,只对 `filename\*` 进行解码。
如果业务代码中直接设置Content-Disposition,需要自行处理多浏览器支持的问题。
-文件下载的更多用法可以通过下载 java-chassis 源码, 查看 DownloadSchema 里面的例子。
-
## 服务消费者开发
消费者统一使用 org.apache.servicecomb.foundation.vertx.http.ReadStreamPart 处理文件下载。
diff --git
a/java-chassis-reference/zh_CN/docs/general-development/file-upload.md
b/java-chassis-reference/zh_CN/docs/general-development/file-upload.md
index f404542..cb48a5d 100644
--- a/java-chassis-reference/zh_CN/docs/general-development/file-upload.md
+++ b/java-chassis-reference/zh_CN/docs/general-development/file-upload.md
@@ -1,24 +1,42 @@
# 文件上传开发指导
+当 HTTP 消息体是 `multipart/form-data`, 表示文件上传。
+
+```text
+GET /apps HTTP/1.1
+Host: 127.0.0.1:8080
+Accept-Language: zh
+Content-Type: multipart/form-data; boundary=----boundary-example----
+
+----boundary-example----
+Content-Disposition: form-data; name="multiPartParamExample1"
+
+contents of multiPartParamExample1
+----boundary-example----
+Content-Disposition: form-data; name="multiPartParamExample2"
+
+contents of multiPartParamExample2
+----boundary-example----
+```
+
+>>> multipart/form-data 也可以表示 Form 参数, 和 application/x-www-form-urlencoded
用法一样。为了避免混淆和简洁,建议multipart/form-data专用于文件上传场景。
+
## 服务提供者开发
服务提供者可以采用Spring MVC 或者 Jax RS定义上传接口。
* 采用Spring MVC
```java
-@PostMapping(path = "/fileUpload", produces = MediaType.TEXT_PLAIN_VALUE,
- consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-public String fileUpload(@RequestPart(name = "file") MultipartFile file)
-
-@PostMapping(path = "/fileUpload", produces = MediaType.TEXT_PLAIN_VALUE,
- consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-public String fileUpload(@RequestPart(name = "file") Part file)
+@PostMapping(path = "multiPartSpringMVCExample", consumes =
MediaType.MULTIPART_FORM_DATA_VALUE)
+public String multiPartSpringMVCExample(@RequestPart("multiPartParamExample1")
MultipartFile example1,
+@RequestPart("multiPartParamExample2") MultipartFile example2)
```
文件上传需要通过 @RequestPart 声明参数,参数类型支持 servlet 定义的 javax.servlet.http.Part 类型,也支持
org.springframework.web.multipart.MultipartFile 类型,两种数据类型功能是一致的,MultipartFile
的底
-层也是Part。 两种数据类型可以混合使用,比如第一个参数是Part,第二个参数是MultipartFile。 除了通过
-定义多个参数的方式上传多个文件,也可以通过List或者数组的方式声明上传多个文件。
+层也是Part。 两种数据类型可以混合使用,比如第一个参数是Part,第二个参数是MultipartFile。
+
+除了通过定义多个参数的方式上传多个文件,也可以通过List或者数组的方式声明上传多个文件。
```java
@PostMapping(path = "/fileUpload", produces = MediaType.TEXT_PLAIN_VALUE,
@@ -30,29 +48,32 @@ public String fileUpload(@RequestPart(name = "files")
List<MultipartFile> files
public String fileUpload(@RequestPart(name = "files") MultipartFile[] files)
```
-可以通过@RequestAttribute获取其他额外信息。
+>>> 说明: 使用 List 或者数组,在HTTP参数映射中,多个文件对应于同名的 form 内容,这不是一个好的 API 设计实践。建议非必要不使用
List 或者数组作为文件上传参数。
+
+可以通过 `@RequestAttribute` 获取其他额外信息。
```java
-@PostMapping(path = "/fileUpload", produces = MediaType.TEXT_PLAIN_VALUE,
- consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-public String fileUpload(@RequestPart(name = "files") List<MultipartFile>
files,
+@PostMapping(path = "multiPartSpringMVCExample", consumes =
MediaType.MULTIPART_FORM_DATA_VALUE)
+public String multiPartSpringMVCExample(@RequestPart("multiPartParamExample1")
MultipartFile example1,
+@RequestPart("multiPartParamExample2") MultipartFile example2,
@RequestAttribute("message") String message)
```
-Spring MVC方式定义上传接口还有其他很多方式,可以下载java-chassis源码,并查看UploadSpringmvcSchema里面的示例。
-
+>>> 说明:`@RequestAttribute` 读取 form 内容, 并将 form 内容转换为 String 类型。 由于
`multipart/form-data` 的 Content-Type 可能存在多样性, 建议文件上传场景不使用 `@RequestAttribute`,
而是使用 `@RequestHeader` 传递额外信息。
* 采用Jax RS
```java
-@Path("/fileUpload")
@POST
-@Produces(MediaType.TEXT_PLAIN)
-public String fileUpload(@FormParam(name = "file") Part file)
+@Path("multiPartJAXRSExample")
+@Consumes(MediaType.MULTIPART_FORM_DATA)
+public String multiPartJAXRSExample(@FormParam("multiPartParamExample1") Part
example1,
+@FormParam("multiPartParamExample2") Part example2)
```
-文件上传需要通过 @FormParam 声明参数,参数类型支持servlet定义的javax.servlet.http.Part类型。除了通过
-定义多个参数的方式上传多个文件,也可以通过List或者数组的方式声明上传多个文件。
+文件上传需要通过 @FormParam 声明参数,参数类型支持servlet定义的javax.servlet.http.Part类型。
+
+除了通过定义多个参数的方式上传多个文件,也可以通过List或者数组的方式声明上传多个文件。
```java
@Path("/fileUpload")
@@ -66,20 +87,24 @@ public String fileUpload(@FormParam(name = "files")
List<Part> files)
public String fileUpload(@FormParam(name = "files") Part[] files)
```
+>>> 说明: 使用 List 或者数组,在HTTP参数映射中,多个文件对应于同名的 form 内容。 尽管在非浏览器场景,使用 List
或者数组会带来便利,但是对于浏览器上传场景则不利于理解。建议非必要不使用 List 或者数组作为文件上传参数。
+
可以通过@FormParam获取其他额外信息。
```java
-@Path("/fileUpload")
@POST
-@Produces(MediaType.TEXT_PLAIN)
-public String fileUpload(@FormParam(name = "files") List<Part> files,
+@Path("multiPartJAXRSExample")
+@Consumes(MediaType.MULTIPART_FORM_DATA)
+public String multiPartJAXRSExample(@FormParam("multiPartParamExample1") Part
example1,
+@FormParam("multiPartParamExample2") Part example2,
@FormParam("message") String message)
```
-Jax RS 方式定义上传接口还有其他很多方式,可以下载java-chassis源码,并查看 UploadJaxrsSchema 里面的示例。
+>>> 说明:`@FormParam` 读取 form 内容, 并将 form 内容转换为 String 类型。 由于
`multipart/form-data` 的 Content-Type 可能存在多样性, 建议文件上传场景不使用 `@FormParam`, 而是使用
`@RequestHeader` 传递额外信息。
+
* 业务开发注意事项
-
+
通过MultipartFile或Part打开流后,记得关闭,否则上传的临时文件会无法删除,导致资源泄露和磁盘空间耗尽。
## 开发服务消费者
@@ -110,19 +135,14 @@ HttpEntity<Map<String, Object>> entity = new
HttpEntity<>(map, headers);
String reseult = template.postForObject(url, entity, String.class);
```
-服务消费者不区分服务提供者是 Spring MVC 或者 Jax RS。 在使用透明 RPC 或者 RestTemplate 的时候, 可以使用如下类型
-与服务提供者的文件对应:
+服务消费者不区分服务提供者是 Spring MVC 或者 Jax RS。 在使用透明 RPC 或者 RestTemplate 的时候,
可以使用如下类型与服务提供者的文件对应:
* java.io.File
* javax.servlet.http.Part
* java.io.InputStream
* org.springframework.core.io.Resource
-使用InputStream时,因为是流的方式,此时没有客户端文件名的概念,服务提供者获取到的文件名为null。
-如果既要使用内存数据,又想让producer可以获取客户端文件名,可以使用resource类型,继承
-org.springframework.core.io.ByteArrayResource,且需要实现 getFilename 方法。
-
-一样的,服务消费者还有其他灵活的使用方式,可以下载 java-chassis 代码查看调用示例。
+使用InputStream时,因为是流的方式,此时没有客户端文件名的概念,服务提供者获取到的文件名为null。
如果既要使用内存数据,又想让producer可以获取客户端文件名,可以使用resource类型,继承org.springframework.core.io.ByteArrayResource,且需要实现
getFilename 方法。
## 使用浏览器上传文件
diff --git
a/java-chassis-reference/zh_CN/docs/general-development/upload-download.md
b/java-chassis-reference/zh_CN/docs/general-development/upload-download.md
index 16a3422..f43ac16 100644
--- a/java-chassis-reference/zh_CN/docs/general-development/upload-download.md
+++ b/java-chassis-reference/zh_CN/docs/general-development/upload-download.md
@@ -1,3 +1,5 @@
+# 文件上传下载
+
通过浏览器上传下载文件,是非常普遍的应用场景。java-chassis基于REST提供了上传下载功能:
* 在定义服务提供者的时候,只允许采用Spring MVC 或者 Jax RS模式。 开发服务消费者不受限制,可以使用透明RPC或者RestTemplate。
@@ -10,3 +12,4 @@
* [文件上传开发指导](file-upload.md)
* [文件下载开发指导](file-download.md)
+>>> 注意: 文件上传下载不支持断点续传特性,如果上传下载大文件,需要注意上传下载时间限制,超时会导致文件上传下载失败。
diff --git a/java-chassis-reference/zh_CN/mkdocs.yml
b/java-chassis-reference/zh_CN/mkdocs.yml
index 5c513f2..aef7eba 100644
--- a/java-chassis-reference/zh_CN/mkdocs.yml
+++ b/java-chassis-reference/zh_CN/mkdocs.yml
@@ -57,14 +57,13 @@ nav:
- 定制序列化和反序列化方法: general-development/secret-field.md
- 使用Context传递控制消息: general-development/context.md
- 返回值序列化扩展: general-development/produceprocess.md
- - 跨域资源共享(CORS配置: general-development/CORS.md
+ - 跨域资源共享(CORS): general-development/CORS.md
- 获取熔断与实例隔离告警事件信息: general-development/AlarmEvent.md
- 优雅停机: general-development/shutdown.md
- 异常处理: general-development/error-handling.md
- 微服务实例间多环境隔离: general-development/multienvironment.md
- 线程模型: general-development/thread-model.md
- 配置日志: general-development/config-logs.md
-
- 多样化的通信协议功能:
- 多协议介绍: transports/introduction.md
- REST over HTTP(Vert.x: transports/rest-over-vertx.md