First of all I am french so pardon my english.
I have a rest service which generate and send a PDF file, I would like to get and display the PDF from an action result on the client side. I am fairly new in C# and tried solutions around google, and Stack Overflow, of course, and can't seem to make it work... Also most answers I find save the PDF on the computer but I couldn't find answers about opening it inside the browser or how to combinate it with an ActioResult
The REST service code :
[Route("api/getPdf/{login}/{password}/{id}")]
[HttpGet]
public HttpResponseMessage getPDF(string login, string password, int id)
{
try
{
if (this.Authenticate(login, password) != null)
{
forms forms = db.forms.Find(id);
if (forms == null)
{
var messageNotFound = string.Format("Form with id = {0} not found", id);
HttpError errAut = new HttpError(messageNotFound);
return Request.CreateResponse(HttpStatusCode.NotFound, errAut);
}
var html = XmlHelper.GetHtml(forms.data, forms.annexe.ToString());
// instantiate the html to pdf converter
HtmlToPdf converter = new HtmlToPdf();
// convert the url to pdf
PdfDocument doc = converter.ConvertHtmlString(html);
// save pdf document
MemoryStream ms = new MemoryStream();
doc.Save(ms);
// close pdf document
doc.Close();
var response = new HttpResponseMessage();
response.Content = new ByteArrayContent(ms.ToArray());
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline");
response.Content.Headers.ContentDisposition.FileName = DateTime.Now.ToShortDateString() + ".pdf";
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
var messageAutheticationFailed = string.Format("Authentication failed", id);
HttpError err = new HttpError(messageAutheticationFailed);
return Request.CreateResponse(HttpStatusCode.ProxyAuthenticationRequired, err);
}
catch (Exception exp)
{
if (System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Break();
throw exp;
}
}
The website side code I tried :
public ActionResult pdf(int id)
{
string html = string.Empty;
string url = URL + login + "/" + PASSWORD + "/" + id;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
html = reader.ReadToEnd();
//string resBody
}
return File(html, "application/pdf");
}
Thank you in advance.
Edit: The browser correctly render the PDF if I directly put the URL of the API.
I managed to find a solution :
public ActionResult pdf(int id)
{
string html = string.Empty;
string url = URL + login + "/" + PASSWORD + "/" + id;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
byte[] content = memoryStream.ToArray();
Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
return File(content, "application/pdf");
}
}
}
At first you should understand where the problem actually is, on server or client side.
So that client could correctly recognize response as PDF file, server should set Content-Type header to application/pdf in the response. You correctly used File(html, "application/pdf") method for this purpose. But the piece that confuses me is that override of File method with string parameter takes a file name not the content itself: File(string fileName, string contentType). But from your snippet it looks like html variable contains downloaded content from external REST service. With such implementation server should throw FileNotFoundException. So at first check whether server correctly completes request (status code 200) and sends Content-Type header equal to application/pdf.
Then, when client (a browser) receives the response, it's completely his responsibility to select a behavior for such content. It could propose to save it on disk or render PDF with internal capabilities or using plugin or extension. It varies from browser to browser and could be configured for major browsers. So you could try opening PDF document from some known resource (e.g. this one) and check how your browser will behave. It will save you from searching for issues with your code when your browser just do not render PDFs.
Related
I have a URL (URL for the live feed from client) which when I hit in browser returns the xml response . I have saved this in text file it`s size is 8 MB.
now my problem is that I need to save this response in xml file on server`s drive. from there I will insert this in database. and request needs to be made using code using http-client or rest-sharp library of c# .net 4.5
I am unsure what should I do for above case. can any body suggest me something
With RestSharp, it's right there in the readme:
var client = new RestClient("http://example.com");
client.DownloadData(request).SaveAs(path);
With HttpClient, it's a bit more involved. Have a look at this blog post.
Another option is Flurl.Http (disclaimer: I'm the author). It uses HttpClient under the hood and provides a fluent interface and lots of convenient helper methods, including:
await "http://example.com".DownloadFileAsync(folderPath, "foo.xml");
Get it on NuGet.
It seems SaveAs was discontinued. You can try this
var client = new RestClient("http://example.com")
byte[] response = client.DownloadData(request);
File.WriteAllBytes(SAVE_PATH, response);
In case you want async version
var request = new RestRequest("/resource/5", Method.GET);
var client = new RestClient("http://example.com");
var response = await client.ExecuteTaskAsync(request);
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception($"Unable to download file");
response.RawBytes.SaveAs(path);
Don't keep the file in memory while reading. Write it directly to the disk.
var tempFile = Path.GetTempFileName();
using var writer = File.OpenWrite(tempFile);
var client = new RestClient(baseUrl);
var request = new RestRequest("Assets/LargeFile.7z");
request.ResponseWriter = responseStream =>
{
using (responseStream)
{
responseStream.CopyTo(writer);
}
};
var response = client.DownloadData(request);
Copied from here https://stackoverflow.com/a/59720610/179017.
Add following NuGet package into the current system
dotnet add package RestSharp
Using Bearer Authentication
// Download file from 3rd party API
[HttpGet("[action]")]
public async Task<IActionResult> Download([FromQuery] string fileUri)
{
// Using rest sharp
RestClient client = new RestClient(fileUri);
client.ClearHandlers();
client.AddHandler("*", () => { return new JsonDeserializer(); });
RestRequest request = new RestRequest(Method.GET);
request.AddParameter("Authorization", string.Format("Bearer " + accessToken),
ParameterType.HttpHeader);
IRestResponse response = await client.ExecuteTaskAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
// Read bytes
byte[] fileBytes = response.RawBytes;
var headervalue = response.Headers.FirstOrDefault(x => x.Name == "Content-Disposition")?.Value;
string contentDispositionString = Convert.ToString(headervalue);
ContentDisposition contentDisposition = new ContentDisposition(contentDispositionString);
string fileName = contentDisposition.FileName;
// you can write a own logic for download file on SFTP,Local local system location
//
// If you to return file object then you can use below code
return File(fileBytes, "application/octet-stream", fileName);
}
}
Using Basic Authentication
// Download file from 3rd party API
[HttpGet("[action]")]
public async Task<IActionResult> Download([FromQuery] string fileUri)
{
RestClient client = new RestClient(fileUri)
{
Authenticator = new HttpBasicAuthenticator("your user name", "your password")
};
client.ClearHandlers();
client.AddHandler("*", () => { return new JsonDeserializer(); });
RestRequest request = new RestRequest(Method.GET);
IRestResponse response = await client.ExecuteTaskAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
// Read bytes
byte[] fileBytes = response.RawBytes;
var headervalue = response.Headers.FirstOrDefault(x => x.Name == "Content-Disposition")?.Value;
string contentDispositionString = Convert.ToString(headervalue);
ContentDisposition contentDisposition = new ContentDisposition(contentDispositionString);
string fileName = contentDisposition.FileName;
// you can write a own logic for download file on SFTP,Local local system location
//
// If you to return file object then you can use below code
return File(fileBytes, "application/octet-stream", fileName);
}
}
I am making API in asp.net Web API framework. I want to read file and convert it into bytes and return it to client.But when reading the file, exception occurs, URL format is not supported?
URL with fileName is send by client.I want to get the file from this URL and convert it into bytes. Tell me about , how i do this?
[Route("api/product/v1/displayimage")]
[AllowAnonymous]
[HttpPost]
//[GZipCompression]
public async Task<byte[]> DisplayImage([FromBody] FilesVM model)
{
try
{
var UrlBase = Url.Content(model.BaseURL);
//var UrlBase = Url.Content("~/Images/Users/5-signs-march14");
// MemoryStream workStream = new MemoryStream();
//string contentType = MimeMapping.GetMimeMapping(fileName);
byte[] byteInfo = System.IO.File.ReadAllBytes(UrlBase);
return await Task.FromResult(byteInfo);
}
catch (Exception ex)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
Url.Content only returns a string (http://localhost/Image...). If you want the actual content you will have to download it. Here's an example:
using (var client = new WebClient())
{
return await Task.FromResult(client.DownloadData(Url.Content("~/Images/Users/5-signs-march14")));
}
I'm trying to post a file + some info to a WebApi I control. My problem is that I can't access the file on the WebAPI side, all other fields are OK.
Here is my Console Application code
using (HttpClient client = new HttpClient())
{
using (MultipartFormDataContent content = new MultipartFormDataContent())
{
string filename = "my_filename.png";
content.Add(new StringContent(DateTime.Now.ToString("yyyy-MM-dd")), "data");
byte[] file_bytes = webClient.DownloadData($"https://my_url/my_file.png");
content.Add( new ByteArrayContent(file_bytes), "file");
string requestUri = "http://localhost:51114/api/File";
HttpResponseMessage result = client.PostAsync(requestUri, content).Result;
Console.WriteLine("Upload result {0}", result.StatusCode);
}
}
Here is my WebAPI Code
[HttpPost]
public void Post(IFormFile file, [FromForm] DateTime data)
{
if (file == null || file.Length == 0)
{
Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
// Never reaches this point..... file is null
}
Any pointers on what I might be missing?
If i'm not mistaken, you can submit a file to a WebAPI endpoint sending it as FormData with a Content-Type : multipart/form-data, something like this.
[HttpPost]
[Route("..."]
public void ReceiveFile()
{
System.Web.HttpPostedFile file = HttpContext.Current.Request.Files["keyName"];
System.IO.MemoryStream mem = new System.IO.MemoryStream();
file.InputStream.CopyTo(mem);
byte[] data = mem.ToArray();
// you can replace the MemoryStream with file.saveAs("path") if you want.
}
You can grab out the content and convert it into a byte array in 2 lines of code, assuming you are only sending a single file (Note) its a good idea to use async for file upload so you don't consume as much cpu time:
var provider = await Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider());
var file = provider.Contents.Single();
I am struggling with being able to create a file with its data based on the byte array returned from the WebAPI. The following is my code for making the call to the web api
using (var http = new WebClient())
{
string url = string.Format("{0}api/FileUpload/FileServe?FileID=" + fileID, webApiUrl);
http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
http.DownloadDataCompleted += Http_DownloadDataCompleted;
byte[] json = await http.DownloadDataTaskAsync(url);
}
The api code is
[HttpGet]
[Route("FileServe")]
[Authorize(Roles = "Admin,SuperAdmin,Contractor")]
public async Task<HttpResponseMessage> GetFile(int FileID)
{
using (var repo = new MBHDocRepository())
{
var file = await repo.GetSpecificFile(FileID);
if (file == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var stream = File.Open(file.PathLocator, FileMode.Open);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
return response;
}
}
I receive a byte array as a response however am unable to create the corresponding file from that byte array. I have no idea how to convert the byte array into the relevant file type (such as jpg, or pdf based on file type in the web api). any help will be appreciated.
Alright so there are a few ways of solving your problem firstly, on the server side of things you can either simply send the content type and leave it at that or you can also send the complete filename which helps you even further.
I have removed the code that is specific to your stuff with basic test code, please just ignore that stuff and use it in terms of your code.
Some design notes here:
[HttpGet]
[Route("FileServe")]
[Authorize(Roles = "Admin,SuperAdmin,Contractor")]
public async Task<HttpResponseMessage> GetFileAsync(int FileID) //<-- If your method returns Task have it be named with Async in it
{
using (var repo = new MBHDocRepository())
{
var file = await repo.GetSpecificFile(FileID);
if (file == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var stream = File.Open(file.PathLocator, FileMode.Open);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName=Path.GetFileName(file.PathLocator)};
return response;
}
}
Your client side code has two options here:
static void Main(string[] args)
{
using (var http = new WebClient())
{
string url = string.Format("{0}api/FileUpload/FileServe?FileID={1}",webApiUrl, fileId);
http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
var response = http.OpenRead(url);
var fs = new FileStream(String.Format(#"C:\Users\Bailey Miller\Downloads\{0}", GetName(http.ResponseHeaders)), FileMode.Create);
response.CopyTo(fs); <-- how to move the stream to the actual file, this is not perfect and there are a lot of better examples
fs.Flush();
fs.Close();
}
}
private static object GetName(WebHeaderCollection responseHeaders)
{
var c_type = responseHeaders.GetValues("Content-Type"); //<-- do a switch on this and return a really weird file name with the correct extension for the mime type.
var cd = responseHeaders.GetValues("Content-Disposition")[0].Replace("\"", ""); <-- this gets the attachment type and filename param, also removes illegal character " from filename if present
return cd.Substring(cd.IndexOf("=")+1); <-- extracts the file name
}
The front end seems to post fine and my routing seems to be working as my API seems to get the request and the filename is valid. However, when I attempt await request.Content.ReadAsStreamAsync(); it doesn't seem to read anything and I get a
Data at the root level is invalid. Line 1, position 1.
exception when de-serializing...
Been at this for awhile trying the different solutions available with no success.. please help.
Things I've tried:
Different content-types
Different webclient methods to upload (uploadstring, uploadfile, uploaddata)
Using WebRequest instead of webclient (I will consistently get an unsupported media type message as a response)
My code:
public string CreateJob()
{
string test = "";
string sURL = "http://" + _domain + _apiRoot + "CreateJob/OrderXML";
try
{
var ms = new MemoryStream();
using (FileStream src = System.IO.File.Open(#"C:\XML\PageDNAOrderXML.txt", FileMode.Open))
{
src.CopyTo(ms);
}
using (WebClient wc = new WebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = "text/xml";
wc.Headers[HttpRequestHeader.AcceptEncoding] = "Encoding.UTF8";
//wc.Headers[HttpRequestHeader.ContentLength] = ms.Length.ToString();
wc.UploadString(sURL, ms.ToString());
}
}
catch (Exception ex)
{
test = ex.Message;
}
return test;
}
RESTful API
[System.Web.Http.HttpPost]
public async void CreateJob(string filename)
{
string success = "false";
try
{
//XDocument doc = XDocument.Load
XmlSerializer xmls = new XmlSerializer(typeof(PdnaXmlParse));
Stream reqStream = await Request.Content.ReadAsStreamAsync();
PdnaXmlParse res = (PdnaXmlParse)xmls.Deserialize(new XmlTextReader(reqStream));
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
}