Repository: quarkusio/quarkus
Issue: PartFilename does not work for InputStream

### Describe the bug

I use quarkus-rest-client-reactive to send a multipart rest request.

This is the model I use:
```
public class GreetingModel {
    @FormParam("file")
    @PartType(value = MediaType.APPLICATION_OCTET_STREAM)
    @PartFilename("static-file-name")
    public InputStream file;
}
```

When I send this request, the filename of the part is not transmitted.

It works if I change the type from InputStream to byte array.
Imo. This annotation should be usable on every type, not only for byte array, or at least an error message should be printed.

### Expected behavior

file name is transmitted for inputstream.

### Actual behavior

null is received for the filename

### How to Reproduce?

Reproducer: 
[rcr-remove-filter (2).zip](https://github.com/quarkusio/quarkus/files/11038862/rcr-remove-filter.2.zip)


1. mvn quarkus:dev
2. open http://localhost:8080/hello/2 in a browser
3. null|Hello is returned
4. Change the GreetingModel to use a byte[] instead of inputstream
5. open http://localhost:8080/hello/2 in a browser
6. static-file-name|Hello is printed


### Output of `uname -a` or `ver`

MINGW64_NT-10.0-19044 NANBCHL9NG3 3.3.6-341.x86_64 2022-09-05 20:28 UTC x86_64 Msys

### Output of `java -version`

openjdk 17.0.4 2022-07-19 OpenJDK Runtime Environment Temurin-17.0.4+8 (build 17.0.4+8) OpenJDK 64-Bit Server VM Temurin-17.0.4+8 (build 17.0.4+8, mixed mode, sharing)

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.16.4.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.4 (9b656c72d54e5bacbed989b64718c159fe39b537) Maven home: C:\\eclipse\\tools\\java\\maven Java version: 17.0.4, vendor: Eclipse Adoptium, runtime: C:\\eclipse\\tools\\java\\17 Default locale: de_DE, platform encoding: Cp1252 OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

### Additional information

_No response_

Changed files:
extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java
independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java
extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java
independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java

diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
index 7f671a29636..73d84c07354 100644
--- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
+++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
@@ -17,6 +17,7 @@
 
 import java.io.Closeable;
 import java.io.File;
+import java.io.InputStream;
 import java.lang.reflect.Modifier;
 import java.nio.file.Path;
 import java.util.AbstractMap;
@@ -1649,6 +1650,9 @@ private void handleMultipartField(String formParamName, String partType, String
         } else if (type.equals(Path.class.getName())) {
             // and so is path
             addFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue);
+        } else if (type.equals(InputStream.class.getName())) {
+            // and so is path
+            addInputStream(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, type);
         } else if (type.equals(Buffer.class.getName())) {
             // and buffer
             addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, errorLocation);
@@ -1663,8 +1667,12 @@ private void handleMultipartField(String formParamName, String partType, String
                     fieldValue);
             addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, buffer, errorLocation);
         } else if (parameterGenericType.equals(MULTI_BYTE_SIGNATURE)) {
-            addMultiAsFile(ifValueNotNull, multipartForm, formParamName, partType, fieldValue, errorLocation);
+            addMultiAsFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, errorLocation);
         } else if (partType != null) {
+            if (partFilename != null) {
+                log.warnf("Using the @PartFilename annotation is unsupported on the type '%s'. Problematic field is: '%s'",
+                        partType, formParamName);
+            }
             // assume POJO:
             addPojo(ifValueNotNull, multipartForm, formParamName, partType, fieldValue, type);
         } else {
@@ -1677,6 +1685,17 @@ private void handleMultipartField(String formParamName, String partType, String
         }
     }
 
+    private void addInputStream(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
+            String partType, String partFilename, ResultHandle fieldValue, String type) {
+        methodCreator.assign(multipartForm,
+                methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "entity",
+                        QuarkusMultipartForm.class, String.class, String.class, Object.class, String.class, Class.class),
+                        multipartForm, methodCreator.load(formParamName), methodCreator.load(partFilename), fieldValue,
+                        methodCreator.load(partType),
+                        // FIXME: doesn't support generics
+                        methodCreator.loadClassFromTCCL(type)));
+    }
+
     private void addPojo(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
             String partType, ResultHandle fieldValue, String type) {
         methodCreator.assign(multipartForm,
@@ -1780,12 +1799,17 @@ private void addString(BytecodeCreator methodCreator, AssignableResultHandle mul
     }
 
     private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
-            String partType,
+            String partType, String partFilename,
             ResultHandle multi, String errorLocation) {
         // they all default to plain/text except buffers/byte[]/Multi<Byte>/File/Path
         if (partType == null) {
             partType = MediaType.APPLICATION_OCTET_STREAM;
         }
+        String filename = partFilename;
+        if (filename == null) {
+            filename = formParamName;
+        }
+
         if (partType.equalsIgnoreCase(MediaType.APPLICATION_OCTET_STREAM)) {
             methodCreator.assign(multipartForm,
                     // MultipartForm#binaryFileUpload(String name, String filename,  Multi<Byte> content, String mediaType);
@@ -1794,7 +1818,7 @@ private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandl
                             MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "multiAsBinaryFileUpload",
                                     QuarkusMultipartForm.class, String.class, String.class, Multi.class,
                                     String.class),
-                            multipartForm, methodCreator.load(formParamName), methodCreator.load(formParamName),
+                            multipartForm, methodCreator.load(formParamName), methodCreator.load(filename),
                             multi, methodCreator.load(partType)));
         } else {
             methodCreator.assign(multipartForm,
@@ -1804,7 +1828,7 @@ private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandl
                             MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "multiAsTextFileUpload",
                                     QuarkusMultipartForm.class, String.class, String.class, Multi.class,
                                     String.class),
-                            multipartForm, methodCreator.load(formParamName), methodCreator.load(formParamName),
+                            multipartForm, methodCreator.load(formParamName), methodCreator.load(filename),
                             multi, methodCreator.load(partType)));
         }
     }
diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java
index e4e8aa704f8..74c00adfe85 100644
--- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java
+++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java
@@ -3,6 +3,7 @@
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -10,6 +11,7 @@
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.util.ArrayList;
 import java.util.stream.Collectors;
 
 import jakarta.enterprise.context.ApplicationScoped;
@@ -29,6 +31,7 @@
 
 import io.quarkus.test.QuarkusUnitTest;
 import io.quarkus.test.common.http.TestHTTPResource;
+import io.smallrye.mutiny.Multi;
 
 public class MultipartFilenameTest {
 
@@ -72,6 +75,39 @@ void shouldUseFileNameFromAnnotationUsingString() {
         assertThat(client.postMultipartWithPartFilenameUsingString(form)).isEqualTo("clientFile:file content");
     }
 
+    @Test
+    void shouldUseFileNameFromAnnotationUsingByteArray() {
+        Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
+
+        ClientFormUsingByteArray form = new ClientFormUsingByteArray();
+        form.file = "file content".getBytes(StandardCharsets.UTF_8);
+        assertThat(client.postMultipartWithPartFilenameUsingByteArray(form)).isEqualTo("clientFile:file content");
+    }
+
+    @Test
+    void shouldUseFileNameFromAnnotationUsingInputStream() {
+        Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
+
+        ClientFormUsingInputStream form = new ClientFormUsingInputStream();
+        form.file = new ByteArrayInputStream("file content".getBytes(StandardCharsets.UTF_8));
+        assertThat(client.postMultipartWithPartFilenameUsingInputStream(form)).isEqualTo("clientFile:file content");
+    }
+
+    @Test
+    void shouldUseFileNameFromAnnotationUsingMultiByte() {
+        Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
+
+        var list = new ArrayList<Byte>();
+        var array = "file content".getBytes(StandardCharsets.UTF_8);
+        for (var b : array) {
+            list.add(b);
+        }
+
+        ClientFormUsingMultiByte form = new ClientFormUsingMultiByte();
+        form.file = Multi.createFrom().items(list.stream());
+        assertThat(client.postMultipartWithPartFilenameUsingMultiByte(form)).isEqualTo("clientFile:file content");
+    }
+
     @Test
     void shouldCopyFileContentToString() throws IOException {
         Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
@@ -121,7 +157,7 @@ public String upload(@MultipartForm FormData form) {
         }
 
         @POST
-        @Path("/using-string")
+        @Path("/using-form-data")
         @Consumes(MediaType.MULTIPART_FORM_DATA)
         public String uploadWithFileContentUsingString(@MultipartForm FormData form) throws IOException {
             return form.myFile.fileName() + ":" + Files.readString(form.myFile.uploadedFile());
@@ -185,10 +221,25 @@ public interface Client {
         String postMultipartWithPartFilename(@MultipartForm ClientFormUsingFile clientForm);
 
         @POST
-        @Path("/using-string")
+        @Path("/using-form-data")
         @Consumes(MediaType.MULTIPART_FORM_DATA)
         String postMultipartWithPartFilenameUsingString(@MultipartForm ClientFormUsingString clientForm);
 
+        @POST
+        @Path("/using-form-data")
+        @Consumes(MediaType.MULTIPART_FORM_DATA)
+        String postMultipartWithPartFilenameUsingByteArray(@MultipartForm ClientFormUsingByteArray clientForm);
+
+        @POST
+        @Path("/using-form-data")
+        @Consumes(MediaType.MULTIPART_FORM_DATA)
+        String postMultipartWithPartFilenameUsingInputStream(@MultipartForm ClientFormUsingInputStream clientForm);
+
+        @POST
+        @Path("/using-form-data")
+        @Consumes(MediaType.MULTIPART_FORM_DATA)
+        String postMultipartWithPartFilenameUsingMultiByte(@MultipartForm ClientFormUsingMultiByte clientForm);
+
         @POST
         @Path("/file-content")
         @Consumes(MediaType.MULTIPART_FORM_DATA)
@@ -228,4 +279,31 @@ public static class ClientFormUsingString {
         @PartFilename(FILE_NAME)
         public String file;
     }
+
+    public static class ClientFormUsingByteArray {
+        public static final String FILE_NAME = "clientFile";
+
+        @FormParam("myFile")
+        @PartType(MediaType.APPLICATION_OCTET_STREAM)
+        @PartFilename(FILE_NAME)
+        public byte[] file;
+    }
+
+    public static class ClientFormUsingInputStream {
+        public static final String FILE_NAME = "clientFile";
+
+        @FormParam("myFile")
+        @PartType(MediaType.APPLICATION_OCTET_STREAM)
+        @PartFilename(FILE_NAME)
+        public InputStream file;
+    }
+
+    public static class ClientFormUsingMultiByte {
+        public static final String FILE_NAME = "clientFile";
+
+        @FormParam("myFile")
+        @PartType(MediaType.APPLICATION_OCTET_STREAM)
+        @PartFilename(FILE_NAME)
+        public Multi<Byte> file;
+    }
 }
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java
index 2a082ffa09e..36ce207fcab 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java
@@ -50,7 +50,11 @@ public QuarkusMultipartForm attribute(String name, String value, String filename
     }
 
     public QuarkusMultipartForm entity(String name, Object entity, String mediaType, Class<?> type) {
-        pojos.add(new PojoFieldData(name, entity, mediaType, type, parts.size()));
+        return entity(name, null, entity, mediaType, type);
+    }
+
+    public QuarkusMultipartForm entity(String name, String filename, Object entity, String mediaType, Class<?> type) {
+        pojos.add(new PojoFieldData(name, filename, entity, mediaType, type, parts.size()));
         parts.add(null); // make place for ^
         return this;
     }
@@ -132,19 +136,22 @@ public void preparePojos(RestClientRequestContext context) throws IOException {
                     break;
                 }
             }
-            parts.set(pojo.position, new QuarkusMultipartFormDataPart(pojo.name, value, pojo.mediaType, pojo.type));
+            parts.set(pojo.position,
+                    new QuarkusMultipartFormDataPart(pojo.name, pojo.filename, value, pojo.mediaType, pojo.type));
         }
     }
 
     public static class PojoFieldData {
         private final String name;
+        private final String filename;
         private final Object entity;
         private final String mediaType;
         private final Class<?> type;
         private final int position;
 
-        public PojoFieldData(String name, Object entity, String mediaType, Class<?> type, int position) {
+        public PojoFieldData(String name, String filename, Object entity, String mediaType, Class<?> type, int position) {
             this.name = name;
+            this.filename = filename;
             this.entity = entity;
             this.mediaType = mediaType;
             this.type = type;
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java
index c5f8b9e70e3..64b46f9f3f6 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java
@@ -20,7 +20,12 @@ public class QuarkusMultipartFormDataPart {
     private final Multi<Byte> multiByteContent;
 
     public QuarkusMultipartFormDataPart(String name, Buffer content, String mediaType, Class<?> type) {
+        this(name, null, content, mediaType, type);
+    }
+
+    public QuarkusMultipartFormDataPart(String name, String filename, Buffer content, String mediaType, Class<?> type) {
         this.name = name;
+        this.filename = filename;
         this.content = content;
         this.mediaType = mediaType;
         this.type = type;
@@ -37,7 +42,6 @@ public QuarkusMultipartFormDataPart(String name, Buffer content, String mediaTyp
         }
         this.isObject = true;
         this.value = null;
-        this.filename = null;
         this.pathname = null;
         this.text = false;
     }
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java
index 03514b349e5..21bb2a23db3 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java
@@ -68,8 +68,12 @@ public FileUpload createFileUpload(HttpRequest request, String name, String file
             if (formDataPart.isAttribute()) {
                 encoder.addBodyAttribute(formDataPart.name(), formDataPart.value());
             } else if (formDataPart.isObject()) {
-                MemoryFileUpload data = new MemoryFileUpload(formDataPart.name(), "", formDataPart.mediaType(),
-                        formDataPart.isText() ? null : "binary", null, formDataPart.content().length());
+                MemoryFileUpload data = new MemoryFileUpload(formDataPart.name(),
+                        formDataPart.filename() != null ? formDataPart.filename() : "",
+                        formDataPart.mediaType(),
+                        formDataPart.isText() ? null : "binary",
+                        null,
+                        formDataPart.content().length());
                 data.setContent(formDataPart.content().getByteBuf());
                 encoder.addBodyHttpData(data);
             } else if (formDataPart.multiByteContent() != null) {