Line data Source code
1 : using System;
2 : using System.Collections;
3 : using System.Collections.Generic;
4 : using System.Linq;
5 : using System.Runtime.Serialization;
6 : using System.Text;
7 : using Cqrs.WebApi.Formatters.FormMultipart.Infrastructure;
8 : using Cqrs.WebApi.Formatters.FormMultipart.Infrastructure.Extensions;
9 :
10 : namespace Cqrs.WebApi.Formatters.FormMultipart.Converters
11 : {
12 : public class ObjectToMultipartDataByteArrayConverter
13 0 : {
14 : private MultipartFormatterSettings Settings { get; set; }
15 :
16 0 : public ObjectToMultipartDataByteArrayConverter(MultipartFormatterSettings settings)
17 : {
18 : if (settings == null)
19 : throw new ArgumentNullException("settings");
20 :
21 : Settings = settings;
22 : }
23 :
24 0 : public byte[] Convert(object value, string boundary)
25 : {
26 : if(value == null)
27 : throw new ArgumentNullException("value");
28 : if (String.IsNullOrWhiteSpace(boundary))
29 : throw new ArgumentNullException("boundary");
30 :
31 : List<KeyValuePair<string, object>> propertiesList = ConvertObjectToFlatPropertiesList(value);
32 :
33 : byte[] buffer = GetMultipartFormDataBytes(propertiesList, boundary);
34 : return buffer;
35 : }
36 :
37 : private List<KeyValuePair<string, object>> ConvertObjectToFlatPropertiesList(object value)
38 : {
39 : var propertiesList = new List<KeyValuePair<string, object>>();
40 : if (value is FormData)
41 : {
42 : FillFlatPropertiesListFromFormData((FormData) value, propertiesList);
43 : }
44 : else
45 : {
46 : FillFlatPropertiesListFromObject(value, "", propertiesList);
47 : }
48 :
49 : return propertiesList;
50 : }
51 :
52 : private void FillFlatPropertiesListFromFormData(FormData formData, List<KeyValuePair<string, object>> propertiesList)
53 : {
54 : foreach (var field in formData.Fields)
55 : {
56 : propertiesList.Add(new KeyValuePair<string, object>(field.Name, field.Value));
57 : }
58 : foreach (var field in formData.Files)
59 : {
60 : propertiesList.Add(new KeyValuePair<string, object>(field.Name, field.Value));
61 : }
62 : }
63 :
64 : private void FillFlatPropertiesListFromObject(object obj, string prefix, List<KeyValuePair<string, object>> propertiesList)
65 : {
66 : if (obj != null)
67 : {
68 : Type type = obj.GetType();
69 :
70 : if (obj is IDictionary)
71 : {
72 : var dict = obj as IDictionary;
73 : int index = 0;
74 : foreach (var key in dict.Keys)
75 : {
76 : string indexedKeyPropName = String.Format("{0}[{1}].Key", prefix, index);
77 : FillFlatPropertiesListFromObject(key, indexedKeyPropName, propertiesList);
78 :
79 : string indexedValuePropName = String.Format("{0}[{1}].Value", prefix, index);
80 : FillFlatPropertiesListFromObject(dict[key], indexedValuePropName, propertiesList);
81 :
82 : index++;
83 : }
84 : }
85 : else if (obj is ICollection && !IsByteArrayConvertableToHttpFile(obj))
86 : {
87 : var list = obj as ICollection;
88 : int index = 0;
89 : foreach (var indexedPropValue in list)
90 : {
91 : string indexedPropName = String.Format("{0}[{1}]", prefix, index);
92 : FillFlatPropertiesListFromObject(indexedPropValue, indexedPropName, propertiesList);
93 :
94 : index++;
95 : }
96 : }
97 : else if (type.IsCustomNonEnumerableType())
98 : {
99 : foreach (var propertyInfo in type.GetProperties())
100 : {
101 : if (propertyInfo.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(IgnoreDataMemberAttribute)))
102 : continue;
103 : string propName = string.IsNullOrWhiteSpace(prefix)
104 : ? propertyInfo.Name
105 : : String.Format("{0}.{1}", prefix, propertyInfo.Name);
106 : object propValue = propertyInfo.GetValue(obj);
107 :
108 : FillFlatPropertiesListFromObject(propValue, propName, propertiesList);
109 : }
110 : }
111 : else
112 : {
113 : propertiesList.Add(new KeyValuePair<string, object>(prefix, obj));
114 : }
115 : }
116 : }
117 :
118 : private byte[] GetMultipartFormDataBytes(List<KeyValuePair<string, object>> postParameters, string boundary)
119 : {
120 : if (postParameters == null || !postParameters.Any())
121 : throw new Exception("Cannot convert data to multipart/form-data format. No data found.");
122 :
123 : Encoding encoding = Encoding.UTF8;
124 :
125 : using (var formDataStream = new System.IO.MemoryStream())
126 : {
127 : bool needsClrf = false;
128 :
129 : foreach (var param in postParameters)
130 : {
131 : // Add a CRLF to allow multiple parameters to be added.
132 : // Skip it on the first parameter, add it to subsequent parameters.
133 : if (needsClrf)
134 : formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));
135 :
136 : needsClrf = true;
137 :
138 : if (param.Value is HttpFile || IsByteArrayConvertableToHttpFile(param.Value))
139 : {
140 : HttpFile httpFileToUpload = param.Value is HttpFile
141 : ? (HttpFile) param.Value
142 : : new HttpFile(null, null, (byte[]) param.Value);
143 :
144 : // Add just the first part of this param, since we will write the file data directly to the Stream
145 : string header = string.Format
146 : (
147 : "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
148 : boundary,
149 : param.Key,
150 : httpFileToUpload.FileName ?? param.Key,
151 : httpFileToUpload.MediaType ?? "application/octet-stream"
152 : );
153 :
154 : formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));
155 :
156 : // Write the file data directly to the Stream, rather than serializing it to a string.
157 : formDataStream.Write(httpFileToUpload.Buffer, 0, httpFileToUpload.Buffer.Length);
158 : }
159 : else
160 : {
161 : string objString = "";
162 : if (param.Value != null)
163 : {
164 : var typeConverter = param.Value.GetType().GetToStringConverter();
165 : if (typeConverter != null)
166 : {
167 : objString = typeConverter.ConvertToString(null, Settings.CultureInfo, param.Value);
168 : }
169 : else
170 : {
171 : throw new Exception(String.Format("Type \"{0}\" cannot be converted to string", param.Value.GetType().FullName));
172 : }
173 : }
174 :
175 : string postData = string.Format
176 : (
177 : "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
178 : boundary,
179 : param.Key,
180 : objString
181 : );
182 : formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
183 : }
184 : }
185 :
186 : // Add the end of the request. Start with a newline
187 : string footer = "\r\n--" + boundary + "--\r\n";
188 : formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));
189 :
190 : byte[] formData = formDataStream.ToArray();
191 :
192 : return formData;
193 : }
194 : }
195 :
196 : private bool IsByteArrayConvertableToHttpFile(object value)
197 : {
198 : return value is byte[] && Settings.SerializeByteArrayAsHttpFile;
199 : }
200 : }
201 : }
|