@@ -48,19 +48,35 @@ public MultipartBody() {
4848 */
4949 public <T > void addOrReplacePart (
5050 @ Nonnull final String name , @ Nonnull final String contentType , @ Nonnull final T value ) {
51+ addOrReplacePart (name , contentType , value , null );
52+ }
53+
54+ /**
55+ * Adds or replaces a part in the multipart body.
56+ *
57+ * @param <T> the type of the part to add or replace.
58+ * @param name the name of the part to add or replace.
59+ * @param contentType the content type of the part to add or replace.
60+ * @param value the value of the part to add or replace.
61+ * @param filename the value of the filename directive.
62+ */
63+ public <T > void addOrReplacePart (
64+ @ Nonnull final String name ,
65+ @ Nonnull final String contentType ,
66+ @ Nonnull final T value ,
67+ @ Nullable String filename ) {
5168 Objects .requireNonNull (value );
5269 if (Compatibility .isBlank (contentType ))
5370 throw new IllegalArgumentException ("contentType cannot be blank or empty" );
5471 if (Compatibility .isBlank (name ))
5572 throw new IllegalArgumentException ("name cannot be blank or empty" );
5673
5774 final String normalizedName = normalizePartName (name );
58- originalNames . put ( normalizedName , name );
59- parts .put (normalizedName , new AbstractMap . SimpleEntry <>( contentType , value ) );
75+ Part part = new Part ( name , value , contentType , filename );
76+ parts .put (normalizedName , part );
6077 }
6178
62- private final Map <String , Map .Entry <String , Object >> parts = new HashMap <>();
63- private final Map <String , String > originalNames = new HashMap <>();
79+ private final Map <String , Part > parts = new HashMap <>();
6480
6581 private String normalizePartName (@ Nonnull final String original ) {
6682 return original .toLowerCase (Locale .ROOT );
@@ -75,7 +91,7 @@ private String normalizePartName(@Nonnull final String original) {
7591 if (Compatibility .isBlank (partName ))
7692 throw new IllegalArgumentException ("partName cannot be blank or empty" );
7793 final String normalizedName = normalizePartName (partName );
78- final Map . Entry < String , Object > candidate = parts .get (normalizedName );
94+ final Part candidate = parts .get (normalizedName );
7995 if (candidate == null ) return null ;
8096 return candidate .getValue ();
8197 }
@@ -90,10 +106,7 @@ public boolean removePart(@Nonnull final String partName) {
90106 throw new IllegalArgumentException ("partName cannot be blank or empty" );
91107 final String normalizedName = normalizePartName (partName );
92108 final Object candidate = parts .remove (normalizedName );
93- if (candidate == null ) return false ;
94-
95- originalNames .remove (normalizedName );
96- return true ;
109+ return candidate != null ;
97110 }
98111
99112 /** {@inheritDoc} */
@@ -111,18 +124,23 @@ public void serialize(@Nonnull final SerializationWriter writer) {
111124 if (parts .isEmpty ()) throw new IllegalStateException ("multipart body cannot be empty" );
112125 final SerializationWriterFactory serializationFactory = ra .getSerializationWriterFactory ();
113126 boolean isFirst = true ;
114- for (final Map .Entry <String , Map . Entry < String , Object > > partEntry : parts .entrySet ()) {
127+ for (final Map .Entry <String , Part > partEntry : parts .entrySet ()) {
115128 try {
129+ Part part = partEntry .getValue ();
116130 if (isFirst ) isFirst = false ;
117131 else writer .writeStringValue ("" , "" );
118132 writer .writeStringValue ("" , "--" + getBoundary ());
119- final String partContentType = partEntry . getValue (). getKey ();
133+ final String partContentType = part . getContentType ();
120134 writer .writeStringValue ("Content-Type" , partContentType );
121- writer .writeStringValue (
122- "Content-Disposition" ,
123- "form-data; name=\" " + originalNames .get (partEntry .getKey ()) + "\" " );
135+
136+ String contentDisposition = "form-data; name=\" " + part .getName () + "\" " ;
137+ if (part .getFilename () != null && !part .getFilename ().trim ().isEmpty ()) {
138+ contentDisposition += "; filename=\" " + part .getFilename () + "\" " ;
139+ }
140+ writer .writeStringValue ("Content-Disposition" , contentDisposition );
141+
124142 writer .writeStringValue ("" , "" );
125- final Object objectValue = partEntry . getValue () .getValue ();
143+ final Object objectValue = part .getValue ();
126144 if (objectValue instanceof Parsable ) {
127145 try (final SerializationWriter partWriter =
128146 serializationFactory .getSerializationWriter (partContentType )) {
@@ -151,4 +169,34 @@ public void serialize(@Nonnull final SerializationWriter writer) {
151169 writer .writeStringValue ("" , "" );
152170 writer .writeStringValue ("" , "--" + boundary + "--" );
153171 }
172+
173+ private class Part {
174+ private final String name ;
175+ private final Object value ;
176+ private final String contentType ;
177+ private final String filename ;
178+
179+ Part (String name , Object value , String contentType , String filename ) {
180+ this .name = name ;
181+ this .value = value ;
182+ this .contentType = contentType ;
183+ this .filename = filename ;
184+ }
185+
186+ public String getName () {
187+ return name ;
188+ }
189+
190+ public Object getValue () {
191+ return value ;
192+ }
193+
194+ public String getContentType () {
195+ return contentType ;
196+ }
197+
198+ public String getFilename () {
199+ return filename ;
200+ }
201+ }
154202}
0 commit comments