4

I am trying to upload an image programmatically via an API to another server. The API expects me to upload image in a byte array to be sent in a field: "image_content".

My implementation and calling code is as below. The web request hits the server but the server responds that the image is not present in my web request.

When I run the below code, I am getting error that the image is not present in the request. What am I missing here?

public static class FormUpload
{
    private static readonly Encoding encoding = Encoding.UTF8;
    public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
    {
        string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
        string contentType = "multipart/form-data; boundary=" + formDataBoundary;

    byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);

    return PostForm(postUrl, userAgent, contentType, formData);
}
private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
{
    HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;

    if (request == null)
    {
        throw new NullReferenceException("request is not a http request");
    }

    // Set up the request properties.
    request.Method = "POST";
    request.ContentType = contentType;
    request.UserAgent = userAgent;
    request.ContentLength = formData.Length;


    // Send the form data to the request.
    using (Stream requestStream = request.GetRequestStream())
    {
        requestStream.Write(formData, 0, formData.Length);
        requestStream.Close();
    }

    return request.GetResponse() as HttpWebResponse;
}

private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
{
    Stream formDataStream = new System.IO.MemoryStream();
    bool needsCLRF = false;

    foreach (var param in postParameters)
    { 
        if (param.Value is FileParameter)
        {
            FileParameter fileToUpload = (FileParameter)param.Value;

            // Add just the first part of this param, since we will write the file data directly to the Stream
            string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
                boundary,
                param.Key,
                fileToUpload.FileName ?? param.Key,
                fileToUpload.ContentType ?? "application/octet-stream");

            formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));

            // Write the file data directly to the Stream, rather than serializing it to a string.
            formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
        }
        else
        {
            string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
                boundary,
                param.Key,
                param.Value);
            formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
        }
    }

    // Add the end of the request.  Start with a newline
    string footer = "\r\n--" + boundary + "--\r\n";
    formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));

    // Dump the Stream into a byte[]
    formDataStream.Position = 0;
    byte[] formData = new byte[formDataStream.Length];
    formDataStream.Read(formData, 0, formData.Length);
    formDataStream.Close();

    return formData;
}

public class FileParameter
{
    public byte[] File { get; set; }
    public string FileName { get; set; }
    public string ContentType { get; set; }
    public FileParameter(byte[] file) : this(file, null) { }
    public FileParameter(byte[] file, string filename) : this(file, filename, null) { }
    public FileParameter(byte[] file, string filename, string contenttype)
    {
        File = file;
        FileName = filename;
        ContentType = contenttype;
    }
}

}

The code to call above function is:

// Read file data
FileStream fs = new FileStream("c:\\myimage.jpeg", FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
fs.Close();

// Generate post objects
Dictionary<string, object> postParameters = new Dictionary<string, object>();
postParameters.Add("image_content",data);

// Create request and receive response
string postURL = "myurl";
string userAgent = "Mozilla";
HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters);

// Process response
StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
string fullResponse = responseReader.ReadToEnd();
webResponse.Close();
Response.Write(fullResponse);
milan m
  • 2,164
  • 3
  • 26
  • 40
  • 1
    read this http://stackoverflow.com/questions/1131425/send-a-file-via-http-post-with-c-sharp – Marian Ban Oct 30 '14 at 08:06
  • Did you try using WebReq.ContentType = "image/jpeg"; ? – Mez Oct 30 '14 at 08:20
  • @Mez, Yes. I tried it. – milan m Oct 30 '14 at 08:42
  • Check this link out http://www.dreamincode.net/forums/topic/149368-uploading-an-image-using-httpwebrequest/ – Mez Oct 30 '14 at 10:51
  • @Mez, the link you have mentioned doesn't have a solution. – milan m Oct 31 '14 at 10:00
  • @MajoB, I read the link you provided. It does not show how to post a request with variable. – milan m Nov 01 '14 at 09:10
  • Is really hard to help you without a quickwatch over your code. Are you sure every value is ok? Could you hardcore some values in order to know if there is an exception implicit in other layer? – Leandro Bardelli Nov 02 '14 at 02:43
  • After writing the data to the requestStream. Try setting the streams Position to 0. – Zache Nov 03 '14 at 15:40
  • If I was doing this I would attempt to capture the exact stream being sent either via the debugger or by using something like [Wireshark](https://www.wireshark.org/). You may be able to spot your mistake by doing that. The other option is that your code is fine and there is a bug on the server end, has anything managed to successfully upload a file this way to the server? – Andrew Fraser Nov 10 '14 at 09:47

3 Answers3

3

In my opinion, you should use the MultipartFormDataContent class because it "Provides a container for content encoded using multipart/form-data MIME type.". Try this

    public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, byte[] data)
    {
        string contentType;
        byte[] formData = Program.GetMultipartFormData(data, out contentType);  
        return PostForm(postUrl, userAgent, contentType, formData);
    }

    public static byte[] GetMultipartFormData(byte[] data, out string contentType)
    {
        var byteArrayContent = new ByteArrayContent(data);
        byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
        byteArrayContent.Headers.Add("image_content", "myimage.jpeg");

        var content = new MultipartFormDataContent(String.Format("----------{0:N}", Guid.NewGuid())) { byteArrayContent };
        contentType = content.Headers.ContentType.ToString();

        return content.ReadAsByteArrayAsync().Result;
    }
Jose M.
  • 1,296
  • 17
  • 22
2

I was able to resolve the problem by the use of RestSharp Api mentioned in the stackoverflow question Upload file through c# using JSON request and RestSharp.

Community
  • 1
  • 1
milan m
  • 2,164
  • 3
  • 26
  • 40
1

You all code is fine but you forgot To encode you parameter

try this

string postData = string.Format("--{0}\r\nContent-Disposition: 
form-data; name=\"{1}\"\r\n\r\n{2}",
                boundary,
                HttpUtility.UrlEncode(param.Key),
                HttpUtility.UrlEncode(param.Value));

In case of binary data

HttpUtility.UrlEncode(Convert.ToBase64String(byte[]))

try to use this code to add parameter in your request

NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
outgoingQueryString.Add("uname", "username");
outgoingQueryString.Add("pname", "password");
string postdata = outgoingQueryString.ToString();

and write this postdata in you request