18

I am trying to use the ImageShack API to upload images. To use it, I am supposed to POST the image using multipart/form-data. I did it like ...

var postData = "";
var req = HttpWebRequest.Create("http://www.imageshack.us/upload_api.php");
req.Method = "POST";
req.ContentType = "multipart/form-data";
postData += "key=my_key_here&";
postData += "type=base64&";

// get base64 data from image
byte[] bytes = File.ReadAllBytes(@"D:\tmp\WpfApplication1\WpfApplication1\Images\Icon128.gif");
string encoded = Convert.ToBase64String(bytes);
postData += "fileupload=" + encoded;

byte[] reqData = Encoding.UTF8.GetBytes(postData);
using (Stream dataStream = req.GetRequestStream())
{
    dataStream.Write(reqData, 0, reqData.Length);
}

var res = (HttpWebResponse)req.GetResponse();
var resStream = res.GetResponseStream();
var reader = new StreamReader(resStream);
string resString = reader.ReadToEnd();
txt1.Text = resString;

but ImageShack is complaining that

<links>
    <error id="parameter_missing">Sorry, but we've detected that unexpected data is received. Required parameter 'fileupload' is missing or your post is not multipart/form-data</error>
</links>

FileUpload is present and I am using multipart/form-data whats wrong?

UPDATE:

New Code http://pastebin.com/TN6e0CD8

Post data http://pastebin.com/fYE9fsxs

UPDATE 2

i looked at the other question Multipart forms from C# client. modified my code with boundary, removed the expect 100 header still i cant get it working ...

ServicePointManager.Expect100Continue = false;
var boundary = "-----------------------------28520690214962";
var newLine = Environment.NewLine;
var propFormat = boundary + newLine +
                    "Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine + 
                    "{1}" + newLine + newLine;
var fileHeaderFormat = boundary + newLine +
                        "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + newLine;

var req = (HttpWebRequest)HttpWebRequest.Create("http://jm/php/upload.php");
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data; boundary=" + boundary;

using (var reqStream = req.GetRequestStream()) {
    var reqWriter = new StreamWriter(reqStream);
    var tmp = string.Format(propFormat, "str1", "hello world");
    reqWriter.Write(tmp);
    tmp = string.Format(propFormat, "str2", "hello world 2");
    reqWriter.Write(tmp);
    reqWriter.Write(boundary + "--");
    reqWriter.Flush();
}
var res = req.GetResponse();
using (var resStream = res.GetResponseStream()) {
    var reader = new StreamReader(resStream);
    txt1.Text = reader.ReadToEnd();
}
Community
  • 1
  • 1
Jiew Meng
  • 84,767
  • 185
  • 495
  • 805
  • 1
    possible duplicate of [Upload files with HTTPWebrequest (multipart/form-data)](http://stackoverflow.com/questions/566462/upload-files-with-httpwebrequest-multipart-form-data) – Chris Shaffer Oct 08 '10 at 13:20

3 Answers3

13

I finally got it with the following code ...

var boundary = "------------------------" + DateTime.Now.Ticks;
var newLine = Environment.NewLine;
var propFormat = "--" + boundary + newLine +
                    "Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine + 
                    "{1}" + newLine;
var fileHeaderFormat = "--" + boundary + newLine +
                        "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + newLine;

var req = (HttpWebRequest)HttpWebRequest.Create("http://jm/php/upload.php");
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data; boundary=" + boundary;

using (var reqStream = req.GetRequestStream()) {
    var reqWriter = new StreamWriter(reqStream);
    var tmp = string.Format(propFormat, "str1", "hello world");
    reqWriter.Write(tmp);
    tmp = string.Format(propFormat, "str2", "hello world 2");
    reqWriter.Write(tmp);
    reqWriter.Write("--" + boundary + "--");
    reqWriter.Flush();
}
var res = req.GetResponse();
using (var resStream = res.GetResponseStream()) {
    var reader = new StreamReader(resStream);
    txt1.Text = reader.ReadToEnd();
}

Notice boundaries have to begin with -- {boundary declared in ContentType} and ending boundary must begin & end with -- . in my case, I originally used

var propFormat = boundary + newLine +
                    "Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine + 
                    "{1}" + newLine;

replace it with

var propFormat = "--" + boundary + newLine +
                    "Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine + 
                    "{1}" + newLine;

and everything works

Jiew Meng
  • 84,767
  • 185
  • 495
  • 805
  • 3
    I don't see where you use the `fileHeaderFormat` or where you add the file content. Can you add that as well? – Ε Г И І И О Oct 31 '12 at 15:00
  • 1
    requestWriter.Write(string.Format(fileHeaderFormat, "image", myImage.FileName, myImage.ContentType)); requestWriter.Flush(); requestStream.Write(myImageAsByteArray, 0, (myImageAsByteArray.Length); So you write strings to the request with requestWriter but then just write your byte[] directly. –  Oct 16 '15 at 14:57
  • @user2102611 `reqWriter` and `reqStream`, in this case. Not bad, but `fileHeaderFormat` only has 2 params and is a little simplistic - have to ensure they have the file bytes, too: `byte[] docBytes; using (FileStream fs = File.OpenRead(@"C:\myPic.jpg") { docBytes = new byte[fs.Length]; using (BinaryReader br = new BinaryReader(fs)) { docBytes = br.ReadBytes((int)fs.Length); } } if (docBytes != null) reqStream.Write(docBytes, 0, docBytes.Length);` – vapcguy Feb 21 '20 at 22:58
12

I believe that you are not building the request body correctly. First, you need to include part boundary (random text) in content type header. For example,

Content-Type: multipart/form-data; boundary=----WebKitFormBoundarySkAQdHysJKel8YBM

Now format of request body will be something like

------WebKitFormBoundarySkAQdHysJKel8YBM 
Content-Disposition: form-data;name="key"

KeyValueGoesHere
------WebKitFormBoundarySkAQdHysJKel8YBM 
Content-Disposition: form-data;name="param2"

ValueHere
------WebKitFormBoundarySkAQdHysJKel8YBM 
Content-Disposition: form-data;name="fileUpload"; filename="y1.jpg"
Content-Type: image/jpeg 

[image data goes here]

I will suggest you to use tool such as Fiddler to understand how these requests are built.

VinayC
  • 47,395
  • 5
  • 59
  • 72
  • I am not sure if i still missed out anything but with my request data like shown [@pastebin](http://pastebin.com/vyqyG39A) full code also on [pastebin](http://pastebin.com/g1FDQnhH). I still get the same error – Jiew Meng Oct 08 '10 at 14:52
0

This is nothing like multipart/form-data

  1. There's no boundaries between fields (needed even with one field).
  2. Why are you base-64 encoding?
  3. There's no indication of the content-type of the image.

Take a look at RFC 2388 for the actual format spec. It can also be useful to look at a Fiddler grab of a file upload from a web-page.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • 1
    Yes, VinayC highlighted that to me. I edited the code and it looks like [@pastebin](http://pastebin.com/TN6e0CD8) now. And the generated post data looks like ... [@pastebin](http://pastebin.com/fYE9fsxs) – Jiew Meng Oct 08 '10 at 15:32
  • There's no need for the subsequent newlines after the values. Is base-64ing a requirement specific to the API? That's unusual, but I could see how an API might ask for it. Since the base-64ing is not part of the multiform format, what you are sending is not image/gif but text/plain. Beyond that, as long as the newlines are CRLFs it looks correct. I'd recommend not ending the boundary names with ----- as it makes it harder to check that the final one is the same but with an extra -- – Jon Hanna Oct 08 '10 at 15:54
  • How do i send the image over? I used base64 after I saw it used somewhere. So maybe I am doing it wrong – Jiew Meng Oct 09 '10 at 01:02
  • @jiewmeng, if you say content-type is image/gif then you can take images bytes and write them directly into the response stream. If API demands it to be in base64 encoded then content type cannot be image/gif but rather text/plain. – VinayC Oct 09 '10 at 03:38
  • hmm, did I make any other mistakes? I tried posting to a PHP script (localhost) and I don't seem to have any `$_POST or $_FILE` data – Jiew Meng Oct 09 '10 at 05:29