The below code is working perfect. It serves the specified image.
public HttpResponseMessage Get(string id)
{
string fileName = string.Format("{0}.png", id);
FileStream fileStream = File.Open(System.Web.Hosting.HostingEnvironment.MapPath("~/Images/" + fileName), FileMode.Open);
HttpResponseMessage response = new HttpResponseMessage { Content = new StreamContent(fileStream) };
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpg");
response.Content.Headers.ContentLength = fileStream.Length;
return response;
}
Just to release the resource that I acquired to read the image file I have modified the code as follows.
public HttpResponseMessage Get(string id)
{
string fileName = string.Format("{0}.png", id);
using (FileStream fileStream = File.Open(System.Web.Hosting.HostingEnvironment.MapPath("~/Images/" + fileName), FileMode.Open))
{
HttpResponseMessage response = new HttpResponseMessage { Content = new StreamContent(fileStream) };
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpg");
response.Content.Headers.ContentLength = fileStream.Length;
return response;
}
}
I am getting the error "Error while copying content to a stream". Yes. I am closing the resource before it is streamed.
But the question is how to serve the image and still close the unhandled resource?
Asp.Net Web API 2 and above.
Thank you for your thoughts.
You don't need to worry about releasing FileStream object. It will closed by lower layers of Web API once response is complete. Code mentioned in your first snippet is fine.
Related
I am in the process of creating a proxy server that makes a request to a PDF Blob link then takes the request to setup its HttpResponse Header which we sent to the client. This diagram should explain
As of now, I am successful at making the request to get the pdf content however I am not sure how to send that back to the user. I have followed other Stackoverflow post such as this one : https://stackoverflow.com/a/43232581/10541061
I turn the response message in step 3 of the diagram to a stream and sent it back in the new HttpResponseMessage content.But instead of PDF content , I get a json file
What I want to return to the client
What I am actually returning to the client
Here is the code I am using to create this proxy endpoint
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<HttpResponseMessage> OpenPDF([FromQuery] string url)
{
var _httpClient = _httpClientFactory.CreateClient();
var response = await _httpClient.GetAsync(url);
var stream = await response.Content.ReadAsStreamAsync();
HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(stream);
message.Content.Headers.ContentLength = stream.Length;
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return message;
}
EDIT
Ok so this actually sends back the PDF when I write the proxy like this
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<FileStreamResult> OpenPDF([FromQuery] string url)
{
var fileStream = new MemoryStream();
var _httpClient = _httpClientFactory.CreateClient();
var file = await _httpClient.GetStreamAsync(url).ConfigureAwait(false);
await file.CopyToAsync(fileStream);
fileStream.Position = 0;
return File(fileStream, "application/pdf", "filename.pdf");
}
The problem is I want to update the content-disposition to inline so I can force this to open in the browser instead of downloading.So I decided to take the filestream and injecting that in the httpResponseMessage.content instead but that still didn't work. It would continue to send me a json file
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<HttpResponseMessage> OpenPDF([FromQuery] string url)
{
var fileStream = new MemoryStream();
var _httpClient = _httpClientFactory.CreateClient();
var file = await _httpClient.GetStreamAsync(url).ConfigureAwait(false);
await file.CopyToAsync(fileStream);
fileStream.Position = 0;
HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(fileStream);
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return message;
}
To be honest, I thought defining the content-type should suffice but guess not
This is pretty straight forward for .NET 6... suspect it should be roughly the same for .NET 4x... This uses the NuGet package Azure.Storage.Blobs
https://github.com/Azure/azure-sdk-for-net/blob/Azure.Storage.Blobs_12.13.1/sdk/storage/Azure.Storage.Blobs/README.md
[HttpGet("stream")]
public async Task GetBlobAsync()
{
var url = new Uri("https://path.to.blob.content/xxx");
var blobClient = new BlobClient(url);
Response.Headers.Add("Content-Type", "application/pdf");
Response.Headers.Add("Content-Disposition", #"attachment;filename=""intended file name.pdf""");
await blobClient.DownloadToAsync(Response.Body);
}
for .NET 4x.
try to add:
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("inline")
{
FileName = "filename.pdf"
};
This question already has an answer here:
PDF downloading directly in Google Chrome -- how to display in browser window instead? [closed]
(1 answer)
Closed 5 years ago.
I am able to download and store the pdf document that is being generated but instead of downloading I want to open it in browser.
I have something like this
MemoryStream os = new MemoryStream();
PdfWriter writer = new PdfWriter(os);
var pdfDocument = new PdfDocument(writer);
using (var document = new Document(pdfDocument))
{
//I am adding different sections here
}
var response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new ByteArrayContent(os.ToArray())
};
response.Content.Headers.Add("Content-Type", "application/pdf");
response.Headers.Add("Content-disposition", "attachment;filename=" + "testPDF.pdf");
return response;
The response is further being sent to controller and there it is being downloaded but I want to open in new browser.
For me, "Content-disposition", "attachment;filename" is not working.
My return value is being passed on the controller further where I am storing in blob and continues to download.
public async Task<IActionResult> GenerateDocument(int id)
{
var result = await _applicationService.GenerateDocument(id);
var blobResult = await _applicationService.SaveDocument(id, result.ResponseObject);
IActionResult OnSuccess() =>
new RedirectResult(blobResult.ResponseObject.URI, true);
return HandleResult(OnSuccess, blobResult.Status);
}
You would want to use inline for the content disposition to let the browser know to display it
//...code removed for brevity
var buffer = os.ToArray();
var contentLength = buffer.Length;
var statuscode = HttpStatusCode.OK;
var response = Request.CreateResponse(statuscode);
response.Content = new ByteArrayContent(buffer);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentLength = contentLength;
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=" + "testPDF.pdf", out contentDisposition)) {
response.Content.Headers.ContentDisposition = contentDisposition;
}
return response;
I want to return an image from WebApi endpoint. This is my method:
[System.Web.Http.HttpGet]
public HttpResponseMessage GetAttachment(string id)
{
string dirPath = HttpContext.Current.Server.MapPath(Constants.ATTACHMENT_FOLDER);
string path = string.Format($"{dirPath}\\{id}.jpg");
try
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
var content = new StreamContent(stream);
result.Content = content;
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = Path.GetFileName(path) };
return result;
}
catch (FileNotFoundException ex)
{
_log.Warn($"Image {path} was not found on the server.");
return Request.CreateResponse(HttpStatusCode.NotFound, "Invalid image ID");
}
}
Unfortunately, the file that is downloaded is incomplete. The message in consuming Android app is:
java.io.EOFException: source exhausted prematurely
The problem is most likely that your Android client thinks the download is over before it actually is.
To easily fix this, you can use this method instead which will return the entire file at once (rather than streaming it):
result.Content = new ByteArrayContent(File.ReadAllBytes(path));
Turns out that this was caused by compression, that was set for all responses in this controller. There is GZip encoding set up in controller's constructor:
HttpContext.Current.Response.AppendHeader("Content-Encoding", "gzip");
HttpContext.Current.Response.Filter = new GZipStream(HttpContext.Current.Response.Filter, CompressionMode.Compress);
To solve this, I added these lines to my method
(just after beginning of try block):
// reset encoding and GZip filter
HttpContext.Current.Response.Headers["Content-Encoding"] = "";
HttpContext.Current.Response.Headers["Content-Type"] = "";
// later content type is set to image/jpeg, and default is application/json
HttpContext.Current.Response.Filter = null;
Also, I'm setting content type and length like this:
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
result.Content.Headers.ContentLength = stream.Length;
public HttpResponseMessage getLogoPath(int ID)
{
String urlsubfix = dataManager.getMobileLogoPath(ID);
String urlToReturn = PictureController.urlprefix +urlsubfix;
var response = Request.CreateResponse(HttpStatusCode.Redirect);
response.Headers.Location = new Uri(urlToReturn);
return response;
}
I want to have one of the API call to return an image, how I am accomplishing that is to redirect to another link that has the image, for example :
http://steve.files.wordpress.com/2006/03/Matrix%20tut%202.jpg
I am trying to use PostMan to test this and it gives me back a broken image. I tried derping around and still couldn't find a solution.
-----Edit part of the code that needs this
public HttpResponseMessage GetCustomerBarCodeLogo(String ID)
{
Barcode BC = new Barcode();
Image img =BC.Encode(TYPE.CODE128, "123456789");
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
ImageConverter imageConverter = new ImageConverter();
byte[] resourceByteArray = (byte[])imageConverter.ConvertTo(img, typeof(byte[]));
MemoryStream dataStream = new MemoryStream(resourceByteArray);
result.Content = new StreamContent(dataStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return result;
}
This returns a broken image.
------Newest Update
I sort of found out the problem. What happens is that there is a problem with SSL basic authentication. What happens is when the first time the client wants to pass in a request, the authentication was set correctly. However, when it tries to get an image which is another request after that is triggered by internal code, that request does not have autherizationHeader and it is not authenticated. I don't know where is the 2nd event triggered so I don't know how to manually set that header.
You cannot redirect to response an image in this url, your caller would be able to change the location.
First solution, try using directly the image result for web api, for sample:
public HttpResponseMessage getLogoPath(int id)
{
string pathImage = // a way to get the imagem path..
// create response
HttpResponseMessage response = new HttpResponseMessage();
// set the content (image)
response.Content = new StreamContent(new FileStream(pathImage));
// set the content type for the respective image
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png"); // or jpg, gif..
return response;
}
Or use Moved Status Code, for smaple:
public HttpResponseMessage getLogoPath(int ID)
{
String urlsubfix = dataManager.getMobileLogoPath(ID);
String urlToReturn = PictureController.urlprefix + urlsubfix;
var response = Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location = new Uri(urlToReturn);
return response;
}
Here is an alternate way that might work for you. This is the close the the technique I use.
public HttpResponseMessage GetCustomerBarCodeLogo(String ID)
{
Barcode BC = new Barcode();
Image img =BC.Encode(TYPE.CODE128, "123456789");
var dataStream = new MemoryStream();
img.Save(dataStream, ImageFormat.Png);
dataStream.Position = 0;
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(dataStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return result;
}
You can use the FileResult for example
public FileResult GetLogo(string imageName)
{
return File(Server.MapPath("~/App_Data/Images/") + imageName, "image");
}
In this case the File function takes the file path and content type and returns a file response.
I'm trying the following code to output a image from a asp.net web api, but the response body length is always 0.
public HttpResponseMessage GetImage()
{
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StreamContent(new FileStream(#"path to image"));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
return response;
}
Any tips?
WORKS:
[HttpGet]
public HttpResponseMessage Resize(string source, int width, int height)
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
// Photo.Resize is a static method to resize the image
Image image = Photo.Resize(Image.FromFile(#"d:\path\" + source), width, height);
MemoryStream memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Jpeg);
httpResponseMessage.Content = new ByteArrayContent(memoryStream.ToArray());
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
httpResponseMessage.StatusCode = HttpStatusCode.OK;
return httpResponseMessage;
}
The the following:
Ensure path is correct (duh)
Ensure your routing is correct. Either your Controller is ImageController or you have defined a custom route to support "GetImage" on some other controller.
(You should get a 404 response for this.)
Ensure you open the stream:
var stream = new FileStream(path, FileMode.Open);
I tried something similar and it works for me.
Instead of a ByteArrayContent you can also use a StreamContent class to work more efficient with streams.