Upload file through c# using JSON request and RestSharp

21,254

This was a fight... In the end I discovered two different ways of solving this problem. The irony of it, as so many of coding problems, was that all i had to do was setting the right parameters in first place...Just one missing parameter cost me more than 4 hours..

Both detailed below :

1 - Use RestSharp (the total field shouldn't be there, and the ispaperduplicate field was missing)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RestSharp;
using System.Web.Script.Serialization;
using System.IO;
using System.Net;

namespace RonRestClient
{    

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            string path = @"C:\filename2.pdf";
            //localhost settings
            string requestHost = @"http://localhost:3000/receipts";
            string tagnr = "p94tt7w";
            string machinenr = "2803433";
            string safe_token = "123";

            // Do it with RestSharp

            templateRequest req = new templateRequest();
            req.receipt = new templateRequest.Receipt(tagnr);
            req.machine = new templateRequest.Machine(machinenr, safe_token);

            var request = new RestRequest("/receipts", Method.POST);
            request.AddParameter("receipt[tag_number]", tagnr);
            request.AddParameter("receipt[ispaperduplicate]", 0);
            request.AddParameter("machine[serial_number]", machinenr);
            request.AddParameter("machine[safe_token]", safe_token);
            request.AddFile("receipt[receipt_file]", File.ReadAllBytes(path), Path.GetFileName(path), "application/octet-stream");

            // Add HTTP Headers
            request.AddHeader("Content-type", "application/json");
            request.AddHeader("Accept", "application/json");
            request.RequestFormat = DataFormat.Json;
            //set request Body
            //request.AddBody(req); 


            // execute the request
            //calling server with restClient
            RestClient restClient = new RestClient("http://localhost:3000");
            restClient.ExecuteAsync(request, (response) =>
            {

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    //upload successfull
                    MessageBox.Show("Upload completed succesfully...\n" + response.Content);
                }
                else
                {
                    //error ocured during upload
                    MessageBox.Show(response.StatusCode + "\n" + response.StatusDescription);
                }
            });

        }

    }

}

2 - Use FileStream with HttpWebRequest (thank you Clivant)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RestSharp;
using System.Web.Script.Serialization;
using System.IO;
using System.Net;

namespace RonRestClient
{

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            string path = @"C:\Projectos\My Training Samples\Adobe Sample\RBO1574.pdf";
            //localhost settings
            string requestHost = @"http://localhost:3000/receipts";
            string tagnr = "p94tt7w";
            string machinenr = "2803433";
            string safe_token = "123";

            FileStream fs1 = File.OpenRead(path);
            long filesize = fs1.Length;
            fs1.Close();

            // Create a http request to the server endpoint that will pick up the
            // file and file description.
            HttpWebRequest requestToServerEndpoint =
                (HttpWebRequest)WebRequest.Create(requestHost);

            string boundaryString = "FFF3F395A90B452BB8BEDC878DDBD152";
            string fileUrl = path;

            // Set the http request header \\
            requestToServerEndpoint.Method = WebRequestMethods.Http.Post;
            requestToServerEndpoint.ContentType = "multipart/form-data; boundary=" + boundaryString;
            requestToServerEndpoint.KeepAlive = true;
            requestToServerEndpoint.Credentials = System.Net.CredentialCache.DefaultCredentials;
            requestToServerEndpoint.Accept = "application/json";


            // Use a MemoryStream to form the post data request,
            // so that we can get the content-length attribute.
            MemoryStream postDataStream = new MemoryStream();
            StreamWriter postDataWriter = new StreamWriter(postDataStream);

            // Include value from the tag_number text area in the post data
            postDataWriter.Write("\r\n--" + boundaryString + "\r\n");
            postDataWriter.Write("Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}",
                                    "receipt[tag_number]",
                                    tagnr);

            // Include ispaperduplicate text area in the post data
            postDataWriter.Write("\r\n--" + boundaryString + "\r\n");
            postDataWriter.Write("Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}",
                                    "receipt[ispaperduplicate]",
                                    0);

            // Include value from the machine number in the post data
            postDataWriter.Write("\r\n--" + boundaryString + "\r\n");
            postDataWriter.Write("Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}",
                                    "machine[serial_number]",
                                    machinenr);

            // Include value from the machine token in the post data
            postDataWriter.Write("\r\n--" + boundaryString + "\r\n");
            postDataWriter.Write("Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}",
                                    "machine[safe_token]",
                                    safe_token);

            // Include the file in the post data
            postDataWriter.Write("\r\n--" + boundaryString + "\r\n");
            postDataWriter.Write("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n"
                                    + "Content-Length: \"{2}\"\r\n"
                                    + "Content-Type: application/octet-stream\r\n"
                                    + "Content-Transfer-Encoding: binary\r\n\r\n",
                                    "receipt[receipt_file]",
                                    Path.GetFileName(fileUrl),
                                    filesize);

            postDataWriter.Flush();


            // Read the file
            FileStream fileStream = new FileStream(fileUrl, FileMode.Open, FileAccess.Read);
            byte[] buffer = new byte[1024];
            int bytesRead = 0;
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
            {
                postDataStream.Write(buffer, 0, bytesRead);
            }
            fileStream.Close();

            postDataWriter.Write("\r\n--" + boundaryString + "--\r\n");
            postDataWriter.Flush();

            // Set the http request body content length
            requestToServerEndpoint.ContentLength = postDataStream.Length;

            // Dump the post data from the memory stream to the request stream
            Stream s = requestToServerEndpoint.GetRequestStream();

            postDataStream.WriteTo(s);

            postDataStream.Close();

        }

    }

}
Share:
21,254
MrWater
Author by

MrWater

I'm the surfer without fate Protected by angels inside an open crate Experimenter by design Resting on a restless suit My art is yet to be found And so I shall keep experimenting around.

Updated on July 09, 2022

Comments

  • MrWater
    MrWater almost 2 years

    After managing to load data to my Rails Server through c# (check here to know what i am talking about), i am now trying to upload a file to that same server, along with other data.

    In Ruby, I am able to do this with the code :

    require 'HTTMultiParty'
    
        class ReceiptCreate
            include HTTMultiParty
            # Log to file
            # debug_output File.new("httparty1.log", "w+")
            base_uri "localhost:3000"
            format :json
            headers "Accept" => "application/json"
    
          def initialize
          end
    
          def post(machine_serial,filepath,tag_number,token)
            options = { body: 
                        {receipt:
                            {tag_number:tag_number,
                             receipt_file: File.new(filepath),
                             ispaperduplicate:0
                             },
                        machine:
                            {serial_number: machine_serial,
                             safe_token: token
                             }
                        }               
                    }
            self.class.post('/receipts', options)
          end
        end
    
    receipt = ReceiptCreate.new()
    filename1 = "C:\\filename1.pdf"
    filename2 = "C:\\filename2.pdf"
    response=receipt.post("2803433",filename2,"p94tt7w","123")
    puts response
    

    an when I inspect the parameters on the rails server i see

    Parameters: {"receipt"=>{"tag_number"=>"p94tt7w", "receipt_file"=>#<ActionDispatch::Http::UploadedFile:0x4183ea8 @original_filename="Invoice.pdf", @content_type="application/octet-stream", @headers="Content-Disposition: form-data; name=\"receipt[receipt_file]\"; filename=\"Invoice.pdf\"\r\nContent-Length: 11653\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n", @tempfile=#<File:C:/Users/diogo/AppData/Local/Temp/RackMultipart20130103-18168-efiqia>>, "ispaperduplicate"=>"0"}, "machine"=>{"serial_number"=>"2803433", "safe_token"=>"123"}}
    

    But if i try to do the same with my c# code below

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using RestSharp;
    using System.Web.Script.Serialization;
    using System.IO;
    
    namespace RonRestClient
    {
    
    
        class templateRequest
        {
            public Receipt receipt;
            public class Receipt
            {
                public float total;
                public String tag_number;
                public bool ispaperduplicate = true;
                public byte[] receipt_file;
                public Receipt(float total, String tagnr, string filepath)
                {
                    this.total = total;
                    this.tag_number = tagnr;
                    this.receipt_file = File.ReadAllBytes(filepath);
                }
            };
            public Machine machine;
            public class Machine
            {
                public String serial_number;
                public String safe_token;
                public Machine(String machinenr, String safe_token)
                {
                    this.serial_number = machinenr;
                    this.safe_token = safe_token;
                }
            };
        }
    
    
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
    
                string path = @"C:\filename2.pdf";
                string tagnr = "p94tt7w";
                string machinenr = "2803433";
                string safe_token = "123";
                float total = 100;
    
                templateRequest req = new templateRequest();
                req.receipt = new templateRequest.Receipt(total, tagnr, path);
                req.machine = new templateRequest.Machine(machinenr, safe_token);
                //string json_body = JsonConvert.SerializeObject(req);
                //string json_body = new JavaScriptSerializer().Serialize(req);
    
    
                //var json_body = "{\"receipt\" : {\"total\":"+total+", \"tag_number\":\""+tagnr+"\",\"ispaperduplicate\":true},\"machine\":{\"serial_number\": \""+machinenr+"\",\"safe_token\": \""+safe_token+"\"}}";
    
                var client = new RestClient("http://localhost:3000/receipts");
    
                var request = new RestRequest(Method.POST);
    
    
                //set request Body
                request.AddHeader("Content-type", "application/json");
                request.AddHeader("Accept", "application/json");
                request.RequestFormat = DataFormat.Json;
    
                request.AddBody(req); 
                //request.AddParameter("application/json", json_body, ParameterType.RequestBody);
    
                // easily add HTTP Headers
    
    
                // add files to upload (works with compatible verbs)
                //request.AddFile("receipt/receipt_file",path);
    
                // execute the request
    
                IRestResponse response = client.Execute(request);
                var content = response.Content; // raw content as string
                if(response.ErrorMessage !="") content += response.ErrorMessage;
                response_box.Text = content;
    
    
    
    
            }
        }
    }
    

    I get this

    Parameters: {"receipt"=>{"total"=>100, "tag_number"=>"p94tt7w", "ispaperduplicate"=>true, "receipt_file"=>[37, 80, [n3...nX], 10]}, "machine"=>{"serial_number"=>"2803433", "safe_token"=>"123"}}
    

    This seems to mean basically that Restsharp just thinks that my file is just another field.

    RestSharp seems to have a method to add files request.AddFile("receipt/receipt_file",path);, and i believe that this should probably be the way to go...but when i just try and add the file, i get an error message saying :

    This property cannot be set after writing has started.

    Do I need to set each attribute of the file separately?

    EDIT

    Meanwhile i found this post, changed my code to :

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using RestSharp;
    using System.Web.Script.Serialization;
    using System.IO;
    using System.Net;
    
    namespace RonRestClient
    {
    
    
        class templateRequest
        {
            public Receipt receipt;
            public class Receipt
            {
                //public float total;
                public String tag_number;
                public bool ispaperduplicate = true;
                //public byte[] receipt_file;
                public Receipt(String tagnr)
                {
                    //this.total = total;
                    this.tag_number = tagnr;
                   // this.receipt_file = File.ReadAllBytes(filepath);
                }
            };
            public Machine machine;
            public class Machine
            {
                public String serial_number;
                public String safe_token;
                public Machine(String machinenr, String safe_token)
                {
                    this.serial_number = machinenr;
                    this.safe_token = safe_token;
                }
            };
        }
    
    
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
    
                string path = @"C:\filename2.pdf";
                string tagnr = "p94tt7w";
                string machinenr = "2803433";
                string safe_token = "123";
                float total = 100;
    
                templateRequest req = new templateRequest();
                req.receipt = new templateRequest.Receipt(tagnr);
                req.machine = new templateRequest.Machine(machinenr, safe_token);
    
                var request = new RestRequest("/receipts",Method.POST);
                request.AddParameter("receipt[total]", total);
                request.AddParameter("receipt[tag_number]", tagnr);
                request.AddParameter("machine[serial_number]", machinenr);
                request.AddParameter("machine[safe_token]", safe_token);
                request.AddFile("receipt[receipt_file]", File.ReadAllBytes(path), "Invoice.pdf", "application/octet-stream");
    
                // Add HTTP Headers
                request.AddHeader("Content-type", "application/json");
                request.AddHeader("Accept", "application/json");
                request.RequestFormat = DataFormat.Json;
                //set request Body
                //request.AddBody(req); 
    
    
                // execute the request
                //calling server with restClient
                RestClient restClient = new RestClient("http://localhost:3000");
                restClient.ExecuteAsync(request, (response) =>
                {
    
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        //upload successfull
                        MessageBox.Show("Upload completed succesfully...\n" + response.Content);
                    }
                    else
                    {
                        //error ocured during upload
                        MessageBox.Show(response.StatusCode + "\n" + response.StatusDescription);
                    }
                });
    
            }
    
        }
    
    }
    

    and now am getting the parameters :

    Parameters: {"receipt"=>{"total"=>"100", "tag_number"=>"p94tt7w", "receipt_file"=>#<ActionDispatch::Http::UploadedFile:0x3db42d8 @original_filename="Invoice.pdf", @content_type="application/octet-stream", @headers="Content-Disposition: form-data; name=\"receipt[receipt_file]\"; filename=\"Invoice.pdf\"\r\nContent-Type: application/octet-stream\r\n", @tempfile=#<File:C:/Users/diogo/AppData/Local/Temp/RackMultipart20130103-18168-9mbt3h>>}, "machine"=>{"serial_number"=>"2803433", "safe_token"=>"123"}}
    

    Along with an HTTP 422 - Unprocessable Entity error.

    If I am to compare these parameters with the ones that i have working from the ruby code, now the only difference seems to be that this last message does not have the Content-length and Content-Transfer-Encoding fields...

    Do you have any idea on how i might add the attributes?