-
Notifications
You must be signed in to change notification settings - Fork 0
/
MultipartForm.cs
363 lines (349 loc) · 14 KB
/
MultipartForm.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
using System;
using System.Net;
using System.Text;
using System.IO;
using System.Collections;
namespace InstantBackgroundUploader
{
/// <summary>
/// Allow the transfer of data files using the W3C's specification
/// for HTTP multipart form data. Microsoft's version has a bug
/// where it does not format the ending boundary correctly.
/// Written by: [email protected]
/// </summary>
public class MultipartForm
{
/// <summary>
/// Holds any form fields and values that you
/// wish to transfer with your data.
/// </summary>
private Hashtable coFormFields;
/// <summary>
/// Used mainly to avoid passing parameters to other routines.
/// Could have been local to sendFile().
/// </summary>
protected HttpWebRequest coRequest;
/// <summary>
/// Used if we are testing and want to output the raw
/// request, minus http headers, out to a file.
/// </summary>
System.IO.Stream coFileStream;
/// <summary>
/// Difined to build the form field data that is being
/// passed along with the request.
/// </summary>
static string CONTENT_DISP = "Content-Disposition: form-data; name=";
/// <summary>
/// Allows you to specify the specific version of HTTP to use for uploads.
/// The dot NET stuff currently does not allow you to remove the continue-100 header
/// from 1.1 and 1.0 currently has a bug in it where it adds the continue-100. MS
/// has sent a patch to remove the continue-100 in HTTP 1.0.
/// </summary>
public Version TransferHttpVersion { get { return coHttpVersion; } set { coHttpVersion = value; } }
Version coHttpVersion;
/// <summary>
/// Used to change the content type of the file being sent.
/// Currently defaults to: text/xml. Other options are
/// text/plain or binary
/// </summary>
public string FileContentType { get { return coFileContentType; } set { coFileContentType = value; } }
string coFileContentType;
/// <summary>
/// Initialize our class for use to send data files.
/// </summary>
/// <param name="url">The web address of the recipient of the data transfer.</param>
public MultipartForm(string url)
{
URL = url;
coFormFields = new Hashtable();
ResponseText = new StringBuilder();
BufferSize = 1024 * 10;
BeginBoundary = "ou812--------------8c405ee4e38917c";
TransferHttpVersion = HttpVersion.Version10;
FileContentType = "text/xml";
}
//---------- BEGIN PROPERTIES SECTION ----------
string _BeginBoundary;
/// <summary>
/// The string that defines the begining boundary of
/// our multipart transfer as defined in the w3c specs.
/// This method also sets the Content and Ending
/// boundaries as defined by the w3c specs.
/// </summary>
public string BeginBoundary
{
get { return _BeginBoundary; }
set
{
_BeginBoundary = value;
ContentBoundary = "--" + BeginBoundary;
EndingBoundary = ContentBoundary + "--";
}
}
/// <summary>
/// The string that defines the content boundary of
/// our multipart transfer as defined in the w3c specs.
/// </summary>
protected string ContentBoundary { get { return _ContentBoundary; } set { _ContentBoundary = value; } }
string _ContentBoundary;
/// <summary>
/// The string that defines the ending boundary of
/// our multipart transfer as defined in the w3c specs.
/// </summary>
protected string EndingBoundary
{ get { return _EndingBoundary; } set { _EndingBoundary = value; } }
string _EndingBoundary;
/// <summary>
/// The data returned to us after the transfer is completed.
/// </summary>
public StringBuilder ResponseText
{ get { return _ResponseText; } set { _ResponseText = value; } }
StringBuilder _ResponseText;
/// <summary>
/// The web address of the recipient of the transfer.
/// </summary>
public string URL
{ get { return _URL; } set { _URL = value; } }
string _URL;
/// <summary>
/// Allows us to determine the size of the buffer used
/// to send a piece of the file at a time out the IO
/// stream. Defaults to 1024 * 10.
/// </summary>
public int BufferSize
{ get { return _BufferSize; } set { _BufferSize = value; } }
int _BufferSize;
//---------- END PROPERTIES SECTION ----------
/// <summary>
/// Used to signal we want the output to go to a
/// text file verses being transfered to a URL.
/// </summary>
/// <param name="path"></param>
public void setFilename(string path)
{
coFileStream = new System.IO.FileStream(path, FileMode.Create, FileAccess.Write);
}
/// <summary>
/// Allows you to add some additional field data to be
/// sent along with the transfer. This is usually used
/// for things like userid and password to validate the
/// transfer.
/// </summary>
/// <param name="key">The form field name</param>
/// <param name="str">The form field value</param>
public void setField(string key, string str)
{
coFormFields[key] = str;
}
/// <summary>
/// Determines if we have a file stream set, and returns either
/// the HttpWebRequest stream or the file.
/// </summary>
/// <returns></returns>
public virtual System.IO.Stream getStream()
{
System.IO.Stream io;
if (null == coFileStream)
io = coRequest.GetRequestStream();
else
io = coFileStream;
return io;
}
/// <summary>
/// Here we actually make the request to the web server and
/// retrieve it's response into a text buffer.
/// </summary>
public virtual void getResponse()
{
if (null == coFileStream)
{
System.IO.Stream io;
WebResponse oResponse;
try
{
oResponse = coRequest.GetResponse();
}
catch (WebException web)
{
System.Windows.Forms.MessageBox.Show("Caught web exception: " + web.ToString());
oResponse = web.Response;
}
if (null != oResponse)
{
io = oResponse.GetResponseStream();
StreamReader sr = new StreamReader(io);
string str;
ResponseText.Length = 0;
while ((str = sr.ReadLine()) != null)
ResponseText.Append(str);
oResponse.Close();
}
else
throw new Exception("MultipartForm: Error retrieving server response");
}
}
/// <summary>
/// Transmits a file to the web server stated in the
/// URL property. You may call this several times and it
/// will use the values previously set for fields and URL.
/// </summary>
/// <param name="aFilename">The full path of file being transfered.</param>
public void sendFile(string aFilename)
{
// The live of this object is only good during
// this function. Used mainly to avoid passing
// around parameters to other functions.
coRequest = (HttpWebRequest)WebRequest.Create(URL);
// Set use HTTP 1.0 or 1.1.
coRequest.ProtocolVersion = TransferHttpVersion;
coRequest.Method = "POST";
coRequest.ContentType = "multipart/form-data; boundary=" + BeginBoundary;
coRequest.Headers.Add("Cache-Control", "no-cache");
coRequest.KeepAlive = true;
string strFields = getFormfields();
string strFileHdr = getFileheader(aFilename);
string strFileTlr = getFiletrailer();
FileInfo info = new FileInfo(aFilename);
coRequest.ContentLength = strFields.Length +
strFileHdr.Length +
strFileTlr.Length +
info.Length;
System.IO.Stream io;
io = getStream();
writeString(io, strFields);
writeString(io, strFileHdr);
writeFile(io, aFilename);
writeString(io, strFileTlr);
getResponse();
io.Close();
// End the life time of this request object.
coRequest = null;
}
public void sendFile(System.IO.Stream aFilestream, string fileStreamName)
{
// The live of this object is only good during
// this function. Used mainly to avoid passing
// around parameters to other functions.
coRequest = (HttpWebRequest)WebRequest.Create(URL);
// Set use HTTP 1.0 or 1.1.
coRequest.ProtocolVersion = TransferHttpVersion;
coRequest.Method = "POST";
coRequest.ContentType = "multipart/form-data; boundary=" + BeginBoundary;
coRequest.Headers.Add("Cache-Control", "no-cache");
coRequest.KeepAlive = true;
string strFields = getFormfields();
string strFileHdr = getFileheader(aFilestream, fileStreamName);
string strFileTlr = getFiletrailer();
//FileInfo info = new FileInfo(aFilename);
coRequest.ContentLength = strFields.Length +
strFileHdr.Length +
strFileTlr.Length +
//info.Length;
aFilestream.Length;
System.IO.Stream io;
io = getStream();
writeString(io, strFields);
writeString(io, strFileHdr);
writeFile(io, aFilestream);
writeString(io, strFileTlr);
getResponse();
io.Close();
// End the life time of this request object.
coRequest = null;
}
/// <summary>
/// Mainly used to turn the string into a byte buffer and then
/// write it to our IO stream.
/// </summary>
/// <param name="io">The io stream for output.</param>
/// <param name="str">The data to write.</param>
public void writeString(System.IO.Stream io, string str)
{
byte[] PostData = System.Text.Encoding.ASCII.GetBytes(str);
io.Write(PostData, 0, PostData.Length);
}
/// <summary>
/// Builds the proper format of the multipart data that
/// contains the form fields and their respective values.
/// </summary>
/// <returns>The data to send in the multipart upload.</returns>
public string getFormfields()
{
string str = "";
IDictionaryEnumerator myEnumerator = coFormFields.GetEnumerator();
while (myEnumerator.MoveNext())
{
str += ContentBoundary + "\r\n" +
CONTENT_DISP + '"' + myEnumerator.Key + "\"\r\n\r\n" +
myEnumerator.Value + "\r\n";
}
return str;
}
/// <summary>
/// Returns the proper content information for the
/// file we are sending.
/// </summary>
/// <remarks>
/// Hits Patel reported a bug when used with ActiveFile.
/// Added semicolon after sendfile to resolve that issue.
/// Tested for compatibility with IIS 5.0 and Java.
/// </remarks>
/// <param name="aFilename"></param>
/// <returns></returns>
public string getFileheader(string aFilename)
{
return ContentBoundary + "\r\n" +
CONTENT_DISP +
"\"fileupload\"; filename=\"" +
Path.GetFileName(aFilename) + "\"\r\n" +
"Content-type: " + FileContentType + "\r\n\r\n";
}
public string getFileheader(System.IO.Stream aFilestream, string fileStreamName)
{
return ContentBoundary + "\r\n" +
CONTENT_DISP +
"\"fileupload\"; filename=\"" +
fileStreamName + "\"\r\n" +
"Content-type: " + FileContentType + "\r\n\r\n";
}
/// <summary>
/// Creates the proper ending boundary for the multipart upload.
/// </summary>
/// <returns>The ending boundary.</returns>
public string getFiletrailer()
{
return "\r\n" + EndingBoundary;
}
/// <summary>
/// Reads in the file a chunck at a time then sends it to the
/// output stream.
/// </summary>
/// <param name="io">The io stream to write the file to.</param>
/// <param name="aFilename">The name of the file to transfer.</param>
public void writeFile(System.IO.Stream io, string aFilename)
{
FileStream readIn = new FileStream(aFilename, FileMode.Open, FileAccess.Read);
readIn.Seek(0, SeekOrigin.Begin); // move to the start of the file
byte[] fileData = new byte[BufferSize];
int bytes;
while ((bytes = readIn.Read(fileData, 0, BufferSize)) > 0)
{
// read the file data and send a chunk at a time
io.Write(fileData, 0, bytes);
}
readIn.Close();
}
public void writeFile(System.IO.Stream io, System.IO.Stream aFilestream)
{
aFilestream.Seek(0, SeekOrigin.Begin); // move to the start of the file
byte[] fileData = new byte[BufferSize];
int bytes;
while ((bytes = aFilestream.Read(fileData, 0, BufferSize)) > 0)
{
// read the file data and send a chunk at a time
io.Write(fileData, 0, bytes);
}
aFilestream.Close();
}
}
}