Upload file through c# using JSON request and RestSharp
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();
}
}
}
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, 2022Comments
-
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?