diff --git a/README.md b/README.md index fc86e8e..3592232 100644 --- a/README.md +++ b/README.md @@ -80,18 +80,37 @@ interface SomeApi { ... + // File parameter @RequestLine("POST /send_photo") @Headers("Content-Type: multipart/form-data") void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") File photo); + // byte[] parameter + @RequestLine("POST /send_photo") + @Headers("Content-Type: multipart/form-data") + void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") byte[] photo); + + // FormData parameter + @RequestLine("POST /send_photo") + @Headers("Content-Type: multipart/form-data") + void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") FormData photo); ... } ``` -In example above, we send file in parameter named **photo** with additional field in form **is_public**. +In the example above, the `sendPhoto` method uses the `photo` parameter using three different supported types. -> **IMPORTANT:** You can specify your files in API method by declaring type **File** or **byte[]**. +* `File` will use the File's extension to detect the `Content-Type`. +* `byte[]` will use `application/octet-stream` as `Content-Type`. +* `FormData` will use the `FormData`'s `Content-Type`. + +`FormData` is custom object that wraps a `byte[]` and defines a `Content-Type` like this: + +```java + FormData formData = new FormData("image/png", myDataAsByteArray); + someApi.sendPhoto(true, formData); +``` ### Spring MultipartFile and Spring Cloud Netflix @FeignClient support diff --git a/feign-form/pom.xml b/feign-form/pom.xml index 9cebe27..f749067 100644 --- a/feign-form/pom.xml +++ b/feign-form/pom.xml @@ -37,6 +37,14 @@ limitations under the License. 1.6 + + + com.google.code.findbugs + annotations + 3.0.1 + + + diff --git a/feign-form/src/main/java/feign/form/FormData.java b/feign-form/src/main/java/feign/form/FormData.java new file mode 100644 index 0000000..e30a83b --- /dev/null +++ b/feign-form/src/main/java/feign/form/FormData.java @@ -0,0 +1,43 @@ +/* + * Copyright 2018 Artem Labazin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feign.form; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +/** + * This object encapsulates a byte array and its associated content type. + * Use if if you want to specify the content type of your provided byte array. + */ + +@SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}) +public final class FormData { + private final String contentType; + private final byte[] data; + + public FormData (String contentType, byte[] data) { + this.contentType = contentType; + this.data = data; + } + + public String getContentType () { + return contentType; + } + + public byte[] getData () { + return data; + } +} diff --git a/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java b/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java index e44207b..7cadd5f 100644 --- a/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java +++ b/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java @@ -29,6 +29,7 @@ import feign.codec.Encoder; import feign.form.multipart.ByteArrayWriter; import feign.form.multipart.DelegateWriter; +import feign.form.multipart.FormDataWriter; import feign.form.multipart.ManyFilesWriter; import feign.form.multipart.Output; import feign.form.multipart.ParameterWriter; @@ -57,6 +58,7 @@ public class MultipartFormContentProcessor implements ContentProcessor { public MultipartFormContentProcessor (Encoder delegate) { writers = new ArrayList(6); addWriter(new ByteArrayWriter()); + addWriter(new FormDataWriter()); addWriter(new SingleFileWriter()); addWriter(new ManyFilesWriter()); addWriter(new ParameterWriter()); diff --git a/feign-form/src/main/java/feign/form/multipart/FormDataWriter.java b/feign-form/src/main/java/feign/form/multipart/FormDataWriter.java new file mode 100644 index 0000000..07e70c7 --- /dev/null +++ b/feign-form/src/main/java/feign/form/multipart/FormDataWriter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2018 Artem Labazin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feign.form.multipart; + +import feign.form.FormData; + +import lombok.val; + +public class FormDataWriter extends AbstractWriter { + @Override + public boolean isApplicable (Object value) { + return value instanceof FormData; + } + + @Override + protected void write (Output output, String key, Object value) throws Exception { + val formData = (FormData) value; + writeFileMetadata(output, key, null, formData.getContentType()); + output.write(formData.getData()); + } +} diff --git a/feign-form/src/test/java/feign/form/BasicClientTest.java b/feign-form/src/test/java/feign/form/BasicClientTest.java index d78c1b1..4085f5a 100644 --- a/feign-form/src/test/java/feign/form/BasicClientTest.java +++ b/feign-form/src/test/java/feign/form/BasicClientTest.java @@ -28,6 +28,7 @@ import java.nio.file.Paths; import java.util.Map; import lombok.val; +import org.apache.tomcat.util.http.fileupload.IOUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -158,4 +159,11 @@ public void testUnknownTypeFile() throws Exception { val stringResponse = api.uploadUnknownType(path.toFile()); Assert.assertEquals("application/octet-stream", stringResponse); } + + @Test + public void testFormData() throws Exception { + val formData = new FormData("application/custom-type", "Allo".getBytes("UTF-8")); + val stringResponse = api.uploadFormData(formData); + Assert.assertEquals("application/custom-type", stringResponse); + } } diff --git a/feign-form/src/test/java/feign/form/Server.java b/feign-form/src/test/java/feign/form/Server.java index ad924aa..fe57c1e 100644 --- a/feign-form/src/test/java/feign/form/Server.java +++ b/feign-form/src/test/java/feign/form/Server.java @@ -178,4 +178,15 @@ public ResponseEntity uploadUnknownType (@RequestPart("file") MultipartF : I_AM_A_TEAPOT; return ResponseEntity.status(status).body(file.getContentType()); } + + @PostMapping( + path = "/upload/form_data", + consumes = MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity uploadFormData (@RequestPart("file") MultipartFile file) { + val status = file != null + ? OK + : I_AM_A_TEAPOT; + return ResponseEntity.status(status).body(file.getContentType()); + } } diff --git a/feign-form/src/test/java/feign/form/TestClient.java b/feign-form/src/test/java/feign/form/TestClient.java index d12b73d..ed4b017 100644 --- a/feign-form/src/test/java/feign/form/TestClient.java +++ b/feign-form/src/test/java/feign/form/TestClient.java @@ -16,14 +16,15 @@ package feign.form; +import java.io.File; +import java.util.List; +import java.util.Map; + import feign.Headers; import feign.Param; import feign.QueryMap; import feign.RequestLine; import feign.Response; -import java.io.File; -import java.util.List; -import java.util.Map; /** * @@ -69,4 +70,8 @@ public interface TestClient { @RequestLine("POST /upload/unknown_type") @Headers("Content-Type: multipart/form-data") String uploadUnknownType (@Param("file") File file); + + @RequestLine("POST /upload/form_data") + @Headers("Content-Type: multipart/form-data") + String uploadFormData (@Param("file") FormData formData); }