I am retrieving a PDF file from SharePoint that comes in as text (not plain, encoded). I want to turn this into an actual PDF file to then upload it to another URL via a POST request.
var client2 = new RestClient("https://preactdk.redacted.com/sites/mysite/_api/web/GetFileByServerRelativeUrl('/sites/mysite/Shared%20Documents/testfolder/test.pdf')/$value");
client2.Timeout = -1;
var request2 = new RestRequest(Method.GET);
request2.AddHeader("Authorization", $"Bearer {tokenData.access_token}");
request2.AddHeader("Accept", "application/json;odata=verbose");
IRestResponse response2 = client2.Execute(request2);
string content = response2.Content;
byte[] bytes = Encoding.ASCII.GetBytes(content);
var myfile = System.IO.File.WriteAllBytes("test.pdf", bytes);
var clientUpload = new RestClient("https://mysite.azurewebsites.net/api/Upload");
RestRequest requestUpload = new RestRequest(Method.POST);
//requestUpload.AddHeader("Content-Type", "multipart/form-data");
requestUpload.AddFile("File", bytes, "test.pdf");
var responseUpload = clientUpload.Post(requestUpload);
I've tried to convert it to a byte array but without much success. How do I create the PDF in memory stream or file stream and pass it to the POST request function ? This function takes a pdf file in its body via POST.
What I needed was to use
byte[] bytes = response2.RawBytes;
directly. It worked smoothly and the PDF displays correctly.
Related
I'm trying to use the Fedex API to send SOAP requests and get shipping labels. The developer documentation on Fedex's website isn't very helpful. Does anyone have any sample code showing how to get shipping labels from your requests? Below is what I'm trying - looks like my request works, but my filestream returns a broken pdf. Unless Fedex is responding to my request incorrectly I'm at a loss for where to continue looking.
FedExHelper fedExHelper = new();
// builds the xml for the request
var parsedXml = fedExHelper.BuildShipmentRequest();
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://ws.fedex.com/web-services");
client.DefaultRequestHeaders.Add("Accept", "image/gif, image/jpeg, image/pjpeg, text/plain, text/html, */*"); // for Accept header
HttpRequestMessage request = new();
request.Method = HttpMethod.Post;
request.Content = new StringContent(parsedXml);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
var response = client.Send(request);
return new FileStreamResult(await response.Content.ReadAsStreamAsync(), "application/pdf")
{
FileDownloadName = "test.pdf"
};
}
Also tried the below after reading the xml response. There's a label node that contains a base64 image. Decoding the base64 string still downloads a broken pdf.
var responseText = FedExHelper.SampleResponseText();
XDocument xml = XDocument.Load(new StringReader(responseText));
var label = xml.Root.Descendants().Where(x => x.Name.LocalName == "Label").FirstOrDefault();
var labelParts = label.Descendants().Where(x => x.Name.LocalName == "Parts").FirstOrDefault();
var image = labelParts.Descendants().Where(x => x.Name.LocalName == "Image").FirstOrDefault();
byte[] bytes = Convert.FromBase64String(image.Value);
MemoryStream stream = new(bytes);
return new FileStreamResult(stream, "application/pdf")
{
FileDownloadName = "test.pdf"
};
#Charleh you were absolutely correct. I had the incorrect ImageType value set in the xml I was posting. I can now return PDF labels.
For anyone else looking at this:
I had ZPLII set as the ImageType. This should have been PDF. I was adapting my code from an old dBase program, having no knowledge of working with the Fedex API.
I am making a request to an external API which needs data in a multipart form to be sent in a specific order.
I have two parameters for the request, one is a JsonParameter and the other is a file.
The issue I am having is I need the JsonParameter to come first in the form data request but cannot find a way to do that currently. I have tried simply changing the order I add to the request but this doesn't seem to affect the order when the request is sent.
This is my current code for the request:
var client = new RestClient(baseUrl);
var request = new RestRequest(endpoint, Method.Post);
request.AlwaysMultipartFormData = true;
var json = JsonConvert.SerializeObject(profile);
var jsonParam = new JsonParameter("profile", profile);
request.AddParameter(jsonParam);
var filename = "test.txt";
var bytes = File.ReadAllBytes(filepath);
request.AddFile("file", bytes, filename, "text/plain");
request.AddHeader("X-auth-token", bearerToken);
var res = await client.ExecuteAsync(request);
Is this possible using restsharp so that the JsonParameter will always come first in the request?
We have a created an API for the application which takes the image via POST request process it and sends the result in JSON format.
We tried calling API from different sources like python, postman app, c#. We can successfully call end point using python and postman app but with c# getting error
c# code [Not working]
byte[] img_data = System.IO.File.ReadAllBytes(#"file_path");
string url_ep = "http://ip:port/get";
Dictionary<string, byte[]> fl_image = new Dictionary<string, byte[]>();
fl_image.Add("image", img_data);
string data = JsonConvert.SerializeObject(fl_image);
var dataToSend = Encoding.UTF8.GetBytes(data);
var request = HttpWebRequest.Create(url_ep);
request.ContentType = "application/json";
request.ContentLength = dataToSend.Length;
request.Method = "POST";
request.GetRequestStream().Write(dataToSend, 0, dataToSend.Length);
var response = request.GetResponse();
System.IO.Stream dataStream = response.GetResponseStream();
System.IO.StreamReader reader = new System.IO.StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
python code [working]
import requests
url = 'http://ip:port/get'
fl_image = {'image': open('file_path', 'rb')}
res = requests.post(url, files=fl_image)
print(res.json())
API Endpoint
from flask import Flask, request
import numpy as np
import cv2 as cv
#app.route('/get', methods = ['POST'])
def get_image():
if request.method == 'POST':
file = request.files['image']
# Read file
f = file.read()
# convert string of image data to uint8
f1 = np.fromstring(f, np.uint8)
# decode image
f2 = cv.imdecode(f1,cv.IMREAD_COLOR)
There are several issues with the way you are posting data from C#. The most relevant one is that you are trying to post a file as a JSON object, with file contents as string.
This cannot work: your python server is clearly expecting multipart/form-data as content-type.
I also strongly recommend you to use HttpClient and not the old HttpWebRequest class to send HTTP Requests.
var filePath = #"file_path";
var url = "http://ip:port/get";
using (var client = new HttpClient())
using (var content = new MultipartFormDataContent())
using (var fileStream = File.OpenRead(filePath))
{
var imageContent = new StreamContent(fileStream);
// NOTE: the line below is not required, but useful when you know the media type
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");
content.Add(imageContent, "image", Path.GetFileName(filePath));
var response = await client.PostAsync(url, content);
var stringResponse = await response.Content.ReadAsStringAsync();
// do what you need with the response
}
Other minor issues:
Do not read the entire file in memory (using File.ReadAllBytes), but open a stream for reading instead.
Use async/await when possible, do not block on async code (do not use .Result, .Wait() or .GetAwaiter().GetResult() on Task or Task<T>)
Always call Dispose() on IDisposable objects when you have finished using them (wrapping them inside a using block)
You need to dispose the connections
reader.Close();
dataStream.Close();
response.Close();
Hope this helps
Or try using HttpClient for .net within the using block
I have an Infopath Form Template on Sharepoint, I want to add a button there so when the user clicks on it, it will POST an string to the following Web API. The following web API is tested and returns an excel file as shown:
I want to Post the FileName of the excel file using post request and it is important for me the request method to be POST type. and then the user will download a file with the specified 'FileName'.
Actually i want to use post method because at the next stage i will send the content of the excel file too.
Important Note: I only can use .Net FrameWork 3.5 because this is the only framework supported in InfoPath Form Templates.
[HttpPost]
public HttpResponseMessage Post([FromBody]string FileName)
{
string reqBook = "c:\somefile.xlsx";
//converting Excel(xlsx) file into bytes array
var dataBytes = File.ReadAllBytes(reqBook);
//adding bytes to memory stream
var dataStream = new MemoryStream(dataBytes);
HttpResponseMessage httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new StreamContent(dataStream);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = FileName;
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
return httpResponseMessage;
}
When you perform the HttpPost on the client side, you will want to read the HttpResponseStream to get the byte data of the response stream.
Once you have the response stream data, you can then deserialize it to the type of object in C# you want, or you could alternatively just write it to the disk as
File.WriteAllBytes("someexcel.xlsx",data);
An easy way to do it would be with the HttpClient class.
HttpClient client = new HttpClient();
var response = client.PostAsync("", null).Result;
var content = response.Content.ReadAsByteArrayAsync().Result;
File.WriteAllBytes("excel.xlsx", content);
Just fill in the PostAsync bit with the Url and the content you wish to post.
I am using .Result to keep everything synchronous - but you can use 'await' if you prefer.
If you are working with HttpWebRequests - then the process becomes more complicated, as you need to manage the streams yourself.
The HttpClient will manage and handle it all for you - so I recommend it, unless there is something special it needs to do that it currently does not.
Due to your .Net 3.5 requirement:
private static HttpWebResponse MakeRequest(string url, string postArgument)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "multipart/form-data;";
Stream stream = request.GetRequestStream();
string result = string.Format("arg1={0}", postArgument);
byte[] value = Encoding.UTF8.GetBytes(result);
stream.Write(value, 0, value.Length);
stream.Close();
return (HttpWebResponse)request.GetResponse();
}
You can then do:
var response = MakeRequest("http://mywebsite.com/ProcessExcel", "accounts.xlsx");
And then do
Stream objStream = response .GetResponseStream();
BinaryReader breader = new BinaryReader(objStream);
byte[] data= breader.ReadBytes((int)webresponse.ContentLength);
File.WriteAllBytes("excel.xlsx",data);
I am using RestSharp (version 105.2.3.0 in Visual Studio 2013, .net 4.5) to call a NodeJS hosted webservice. One of the calls I need to make is to upload a file. Using a RESTSharp request, if I retrieve the stream from my end into a byte array and pass that to AddFile, it works fine. However, I would much rather stream the contents and not load up entire files in server memory (the files can be 100's of MB).
If I set up an Action to copy my stream (see below), I get an exception at the "MyStream.CopyTo" line of System.Net.ProtocolViolationException (Bytes to be written to the stream exceed the Content-Length bytes size specified). This exception is thrown within the Action block after client.Execute is called.
From what I read, I should not be manually adding a Content-Length header, and it doesn't help if I do. I have tried setting CopyTo buffer too small and large values, as well as omitting it entirely, to no avail. Can somebody give me a hint on what I've missed?
// Snippet...
protected T PostFile<T>(string Resource, string FieldName, string FileName,
string ContentType, Stream MyStream,
IEnumerable<Parameter> Parameters = null) where T : new()
{
RestRequest request = new RestRequest(Resource);
request.Method = Method.POST;
if (Parameters != null)
{
// Note: parameters are all UrlSegment values
request.Parameters.AddRange(Parameters);
}
// _url, _username and _password are defined configuration variables
RestClient client = new RestClient(_url);
if (!string.IsNullOrEmpty(_username))
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
/*
// Does not work, throws System.Net.ProtocolViolationException,
// Bytes to be written to the stream exceed the
// Content-Length bytes size specified.
request.AddFile(FieldName, (s) =>
{
MyStream.CopyTo(s);
MyStream.Flush();
}, FileName, ContentType);
*/
// This works, but has to load the whole file in memory
byte[] data = new byte[MyStream.Length];
MyStream.Read(data, 0, (int) MyStream.Length);
request.AddFile(FieldName, data, FileName, ContentType);
var response = client.Execute<T>(request);
// check response and continue...
}
I had the same issue. I ended up using the .Add() on the Files collection. It has a FileParameter param which has the same params as AddFile(), you just have to add the ContentLength:
var req = GetRestRequest("Upload", Method.POST, null);
//req.AddFile("file",
// (s) => {
// var stream = input(imageObject);
// stream.CopyTo(s);
// stream.Dispose();
// },
// fileName, contentType);
req.Files.Add(new FileParameter {
Name = "file",
Writer = (s) => {
var stream = input(imageObject);
stream.CopyTo(s);
stream.Dispose();
},
FileName = fileName,
ContentType = contentType,
ContentLength = contentLength
});
The following code works for me for uploading a csv file using rest sharp. Web services API has been called.
var client = new RestClient(<YOUR API END URL >);
var request = new RestRequest(Method.POST) ;
request.AlwaysMultipartFormData = true;
request. AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("X-API-TOKEN", <Your Unique Token - again not needed for certain calls>);
request.AddParameter(<Your parameters.....>);
request.AddFile("file", currentFileLocation, contentType);
request.AddParameter("multipart/form-data", fileName, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
var response = client.Execute(request);