Skip to content

Commit c127d27

Browse files
committed
Web API image upload: Pass through of content disposition multipart header parameters
1 parent 1d1e263 commit c127d27

7 files changed

Lines changed: 66 additions & 34 deletions

File tree

src/Libraries/SmartStore.Services/Catalog/IProductService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ public partial interface IProductService
102102
/// <summary>
103103
/// Gets a product by GTIN
104104
/// </summary>
105-
/// <param name="sku">SKU</param>
105+
/// <param name="gtin">GTIN</param>
106106
/// <returns>Product</returns>
107-
Product GetProductByGtin(string sku);
107+
Product GetProductByGtin(string gtin);
108108

109109
/// <summary>
110110
/// Adjusts inventory

src/Libraries/SmartStore.Services/Catalog/ProductService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,7 @@ public virtual Product GetProductBySku(string sku)
10261026
/// <summary>
10271027
/// Gets a product by GTIN
10281028
/// </summary>
1029-
/// <param name="sku">GTIN</param>
1029+
/// <param name="gtin">GTIN</param>
10301030
/// <returns>Product</returns>
10311031
public virtual Product GetProductByGtin(string gtin)
10321032
{

src/Plugins/SmartStore.WebApi/Controllers/Api/UploadsController.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Collections.Specialized;
34
using System.IO;
45
using System.Linq;
56
using System.Net.Http;
7+
using System.Net.Http.Headers;
68
using System.Threading.Tasks;
79
using System.Web.Http;
810
using SmartStore.Core;
@@ -100,9 +102,13 @@ public async Task<IQueryable<UploadImage>> PostProductImages()
100102
{
101103
FileName = file.Headers.ContentDisposition.FileName.ToUnquoted(),
102104
Name = file.Headers.ContentDisposition.Name.ToUnquoted(),
103-
MediaType = file.Headers.ContentType.MediaType.ToUnquoted()
105+
MediaType = file.Headers.ContentType.MediaType.ToUnquoted(),
106+
ContentDisposition = file.Headers.ContentDisposition.Parameters
104107
};
105108

109+
if (image.FileName.IsEmpty())
110+
image.FileName = entity.Name;
111+
106112
var pictureBinary = File.ReadAllBytes(file.LocalFileName);
107113

108114
if (pictureBinary != null && pictureBinary.Length > 0)
@@ -113,9 +119,6 @@ public async Task<IQueryable<UploadImage>> PostProductImages()
113119

114120
if (pictureBinary != null)
115121
{
116-
if (image.FileName.IsEmpty())
117-
image.FileName = entity.Name;
118-
119122
var seoName = _pictureService.Value.GetPictureSeName(Path.GetFileNameWithoutExtension(image.FileName));
120123

121124
var newPicture = _pictureService.Value.InsertPicture(pictureBinary, image.MediaType, seoName, true, false);

src/Plugins/SmartStore.WebApi/Extensions/MiscExtensions.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,10 @@ internal static class MiscExtensions
3333
{
3434
public static string ToUnquoted(this string value)
3535
{
36-
if (value.HasValue())
36+
if (value.HasValue() && value.Length > 1)
3737
{
38-
if (value.StartsWith("\"") && value.EndsWith("\""))
39-
return value.Trim(new char[] { '"' });
40-
else if (value.StartsWith("'") && value.EndsWith("'"))
41-
return value.Trim(new char[] { '\'' });
38+
if ((value.StartsWith("\"") && value.EndsWith("\"")) || (value.StartsWith("'") && value.EndsWith("'")))
39+
return value.Substring(1, value.Length - 2);
4240
}
4341
return value;
4442
}

src/Plugins/SmartStore.WebApi/Models/Api/UploadImage.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Runtime.Serialization;
1+
using System.Collections.Generic;
2+
using System.Net.Http.Headers;
3+
using System.Runtime.Serialization;
24
using SmartStore.Core.Domain.Media;
35

46
namespace SmartStore.WebApi.Models.Api
@@ -7,19 +9,19 @@ namespace SmartStore.WebApi.Models.Api
79
public partial class UploadImage
810
{
911
/// <summary>
10-
/// Name attribute of content-disposition request header
12+
/// Unquoted name attribute of content-disposition multipart header
1113
/// </summary>
1214
[DataMember]
1315
public string Name { get; set; }
1416

1517
/// <summary>
16-
/// Filename attribute of content-disposition request header
18+
/// Unquoted filename attribute of content-disposition multipart header
1719
/// </summary>
1820
[DataMember]
1921
public string FileName { get; set; }
2022

2123
/// <summary>
22-
/// Media (mime) type of content-type request header
24+
/// Media (mime) type of content-type multipart header
2325
/// </summary>
2426
[DataMember]
2527
public string MediaType { get; set; }
@@ -31,7 +33,7 @@ public partial class UploadImage
3133
public bool Exists { get; set; }
3234

3335
/// <summary>
34-
/// Indicates whether the uploaded image was inserted
36+
/// Indicates whether the uploaded image has been inserted
3537
/// </summary>
3638
[DataMember]
3739
public bool Inserted { get; set; }
@@ -54,6 +56,12 @@ public partial class UploadImage
5456
[DataMember]
5557
public string FullSizeImageUrl { get; set; }
5658

59+
/// <summary>
60+
/// Raw custom parameters of the content-disposition multipart header
61+
/// </summary>
62+
[DataMember]
63+
public ICollection<NameValueHeaderValue> ContentDisposition { get; set; }
64+
5765
/// <summary>
5866
/// The picture entity. Can be null.
5967
/// </summary>

src/Tools/SmartStore.WebApi.Client/Misc/Extensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,10 @@ public static int ToInt(this string value, int defaultValue = 0)
8989
}
9090
return defaultValue;
9191
}
92+
93+
public static string EmptyNull(this string value)
94+
{
95+
return (value ?? string.Empty).Trim();
96+
}
9297
}
9398
}

src/Tools/SmartStore.WebApi.Client/WebApi/WebApiConsumer.cs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Collections.Specialized;
34
using System.IO;
45
using System.Linq;
56
using System.Net;
@@ -31,6 +32,7 @@ private void WriteToStream(MemoryStream stream, StringBuilder requestContent, st
3132
private byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary, StringBuilder requestContent)
3233
{
3334
var needsCLRF = false;
35+
var sb = new StringBuilder();
3436

3537
using (var stream = new MemoryStream())
3638
{
@@ -45,18 +47,23 @@ private byte[] GetMultipartFormData(Dictionary<string, object> postParameters, s
4547

4648
if (param.Value is ApiFileParameter)
4749
{
48-
var fileToUpload = (ApiFileParameter)param.Value;
50+
var file = (ApiFileParameter)param.Value;
4951

50-
string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
51-
boundary,
52-
param.Key,
53-
fileToUpload.FileName ?? param.Key,
54-
fileToUpload.ContentType ?? "application/octet-stream");
52+
sb.Clear();
53+
sb.AppendFormat("--{0}\r\n", boundary);
54+
sb.AppendFormat("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", param.Key, file.FileName ?? param.Key);
55+
56+
foreach (var key in file.Parameters.AllKeys)
57+
{
58+
sb.AppendFormat("; {0}=\"{1}\"", key, file.Parameters[key].Replace('"', '\''));
59+
}
60+
61+
sb.AppendFormat("\r\nContent-Type: {0}\r\n\r\n", file.ContentType ?? "application/octet-stream");
5562

56-
WriteToStream(stream, requestContent, header);
63+
WriteToStream(stream, requestContent, sb.ToString());
5764

58-
stream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
59-
requestContent.AppendFormat("<Binary image data here (length {0} bytes)...>", fileToUpload.File.Length);
65+
stream.Write(file.Data, 0, file.Data.Length);
66+
requestContent.AppendFormat("<Binary image data here (length {0} bytes)...>", file.Data.Length);
6067
}
6168
else
6269
{
@@ -114,6 +121,7 @@ public Dictionary<string, object> CreateProductImageMultipartData(List<string> f
114121
if (!string.IsNullOrEmpty(sku))
115122
dic.Add("Sku", sku);
116123

124+
// also possible:
117125
//if (!string.IsNullOrEmpty(gtin))
118126
// dic.Add("Gtin", sku);
119127

@@ -125,7 +133,14 @@ public Dictionary<string, object> CreateProductImageMultipartData(List<string> f
125133
fstream.Read(data, 0, data.Length);
126134

127135
var name = Path.GetFileName(path);
128-
dic.Add(string.Format("my-image{0}", ++count), new ApiFileParameter(data, name, MimeMapping.GetMimeMapping(name)));
136+
var id = string.Format("my-image{0}", ++count);
137+
var apiFile = new ApiFileParameter(data, name, MimeMapping.GetMimeMapping(name));
138+
139+
// test pass through of custom parameters
140+
apiFile.Parameters.Add("CustomValue1", string.Format("{0:N}", Guid.NewGuid()));
141+
apiFile.Parameters.Add("CustomValue2", string.Format("say hello to {0}", id));
142+
143+
dic.Add(id, apiFile);
129144

130145
fstream.Close();
131146
}
@@ -258,23 +273,26 @@ public bool ProcessResponse(HttpWebRequest webRequest, WebApiConsumerResponse re
258273

259274
public class ApiFileParameter
260275
{
261-
public ApiFileParameter(byte[] file)
262-
: this(file, null)
276+
public ApiFileParameter(byte[] data)
277+
: this(data, null)
263278
{
264279
}
265-
public ApiFileParameter(byte[] file, string filename)
266-
: this(file, filename, null)
280+
public ApiFileParameter(byte[] data, string filename)
281+
: this(data, filename, null)
267282
{
268283
}
269-
public ApiFileParameter(byte[] file, string filename, string contenttype)
284+
public ApiFileParameter(byte[] data, string filename, string contenttype)
270285
{
271-
File = file;
286+
Data = data;
272287
FileName = filename;
273288
ContentType = contenttype;
289+
Parameters = new NameValueCollection();
274290
}
275291

276-
public byte[] File { get; set; }
292+
public byte[] Data { get; set; }
277293
public string FileName { get; set; }
278294
public string ContentType { get; set; }
295+
296+
public NameValueCollection Parameters { get; set; }
279297
}
280298
}

0 commit comments

Comments
 (0)