How do I write an image's MemoryStream to the page body? - asp.net

I'm converting ASP.NET WebForms code to ASP.NET Core Razor pages which is new to me. I'm trying to retrieve an image MemoryStream from a business class (based on SixLabors awesome ImageSharp) and have the page render the JPEG -- no HTML, just the image. I intend to use this page elsewhere as an <img> src, like <img src="Render?imageID=42&mode=invert" />
In Render.cshtml.cs:
public class RenderModel : PageModel
{
public void OnGet()
{
//snip
Stream stream = new MemoryStream();
using (Image image1 = Image.Load(imagePath))
{
SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder();
encoder.Quality = 75;
image1.Save(stream, encoder);
//image.Save("/temp/xxx.jpg", encoder); //test to see image. it works
}
Response.Clear();
//Response.Headers.ContentLength = stream.Length;
Response.ContentType = "image/jpeg";
Response.Body = stream;
}
}
...but this is not working, I get:
System.InvalidOperationException: Response Content-Length mismatch: too few bytes written (0 of 135408).
135408 is the stream.Length.
I'm probably not doing this correctly in the ASP.NET Core/Razor way. Can anyone set me straight as to how to do this? Thanks!
EDIT: commenting out the Headers.ContentLength fixes the error. But now I get a broken-image icon in the browser. Closer...

You need to write to the Response.Body isntead of replacing it.
stream.Seek(0, SeekOrigin.Begin);
await stream.CopyToAsync(Response.Body);
await Response.Body.FlushAsync();

I think Razor pages are intented to return html content.
However it seems to be possible to return different types of result in OnGet e.g. you could return a new FileContentReset (FileStreamResult seems to have issues with contentlength)
// read as bytes
public FileContentResult OnGet()
{
var image = System.IO.File.ReadAllBytes(#"c:\temp\myimage.jpeg");
return new FileContentResult(image.ToArray(), "image/jpeg");
}
// example if image comes from stream
public FileContentResult OnGet()
{
using var imageStr = System.IO.File.Open(#"c:\temp\myimage.jpeg", FileMode.Open);
using var memStr = new MemoryStream();
imageStr.CopyTo(memStr);
return new FileContentResult(memStr.ToArray(), "image/jpeg");
}
Even better maybe it to not use a Razor page and to add a MVC controller to return the result.

Related

.Net Core - Send image through api = Resource interpreted as Document but transferred with MIME type image/jpeg

In the wwwroot folder of my .Net Core MVC application, I have some images. I need to serve these pictures to TopDesk, where I can put in a url which gets embedded. I have no influence on Topdesk. I can only change the way the image is served.
When I use a direct link to the image, it works. The image gets embedded
Example of direct url:
https://my.web.site/images/image001.jpeg
But there is a limited embedded size (600px) so i need to resize the images. For that purpose I wrote a very simple api controller:
[HttpGet]
[Route("api/Images/GetImage/{id}")]
public IActionResult GetImage(string id)
{
try
{
var pad = $"c:\\Images\\{id}";
if(System.IO.File.Exists(path))
{
var fileBytes = System.IO.File.ReadAllBytes(path);
var smallImage = ..... doing resizing;
new FileExtensionContentTypeProvider().TryGetContentType(Path.GetFileName(path), out var contentType);
return File(smallImage , contentType ?? "application/octet-stream", $"{id}");
}
return NotFound();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
but the url
https://my.web.site/api/images/GetImage/image001.jpeg
results in
Resource interpreted as Document but transferred with MIME type
image/jpeg
The image doesn't show.
When i test the url in Postman, it returns the image without warning.
What am i missing here?
Instead of returning a File, try using FileContentResult instead:
[HttpGet]
[Route("api/Images/GetImage/{id}")]
public IActionResult GetImage(string id)
{
try
{
var path = $"c:\\Images\\{id}";
if(System.IO.File.Exists(path))
{
var fileBytes = System.IO.File.ReadAllBytes(path);
var smallImage = ..... doing resizing;
new FileExtensionContentTypeProvider().TryGetContentType(Path.GetFileName(path), out var contentType);
return new FileContentResult(fileBytes, contentType ?? "application/octet-stream");
}
return NotFound();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
When navigating to /GetImage/{id} with a browser, you will see that with File the browser tends to download the file, but with FileContentResult it displays the image in the browser tab directly, which is the same behavior as using static files. This is probably happening because of the Response Headers being added when using File/FileContentResult (probably a Content-Disposition header). Not sure how TopDesk is using these images though.
Off-topic: It's also a good practice to not instantiate a FileExtensionContentTypeProvider with every request. Instead, you can register it as a singleton in your Startup.cs like:
services.AddSingleton(new FileExtensionContentTypeProvider());
and inject it in your controller's constructor.

Aspose.Words Returning PDF as Stream does nothing (ASP.NET Web API)

We are exploring using Aspose.Words for some conversions in an on premise API.
This works perfectly for Excel sheets using Aspose.Cells.
[HttpPost]
[Route("convert/excel")]
public async Task<IActionResult> ConvertExcel(IFormFile fileToConvert)
{
var fileStream = new MemoryStream();
fileToConvert.CopyTo(fileStream);
var convertedFile = await pdfConverter.ConvertExcelAsync(fileStream);
return File(convertedFile, "application/octet-stream");
}
However when using exactly the same method for Aspose.Words it does nothing, literally nothing just continues for a few minutes and then times out. This is not a timeout issue with the conversion as the file is 200KB.
[HttpPost]
[Route("convert/word")]
public async Task<IActionResult> ConvertWord(IFormFile fileToConvert)
{
var fileStream = new MemoryStream();
fileToConvert.CopyTo(fileStream);
var convertedFile = await pdfConverter.ConvertWordAsync(fileStream);
return File(convertedFile, "application/octet-stream");
}
I have tried various forms of returning a file but no luck.
return new FileStreamResult(convertedFile, "application/pdf");
The actual conversion methods look like this.
public Task<Stream> ConvertWordAsync(Stream fileStream)
{
return Task.Factory.StartNew(() => ConvertWord(fileStream));
}
private Stream ConvertWord(Stream inputFile)
{
var doc = new Document(inputFile);
var outputFile = new MemoryStream();
doc.Save(outputFile, Aspose.Words.SaveFormat.Pdf);
//doc.Save(#"C:\ProgramData\foo.pdf", Aspose.Words.SaveFormat.Pdf); //THIS WORKS BUT NOT APPOPRIATE
return outputFile;
}
I have also updated it to support HttpGet and hard-coded a path to a file and in browser just get a Download failed - network error.
Is is possible that the Save method returns the memory stream at the end of the stream.
You should try the following immediately after the call to doc.Save
outputFile.Seek(0, SeekOrigin.Begin);

Web API Async Upload with XmlHttpRequest to get progress

I'm trying to drag and drop file upload with a progress bar.
I have a div which is listening to files being dropped on which is working perfectly.
I'm then..
//Setting up a XmlHttpRequest
xhr = new XMLHttpRequest();
//Open connection
xhr.open("post", "api/ImageUpload", true);
// Set appropriate headers
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Type", uf.type);
xhr.setRequestHeader("X-File-Name", uf.name);
xhr.setRequestHeader("X-File-Size", uf.size);
This sends fine, with the stream as the body of the request to the Web API (not async).
[System.Web.Mvc.HttpPost]
public string Post()
{
Stream stream = HttpContext.Current.Request.InputStream;
String filename = HttpContext.Current.Request.Headers["X-File-Name"];
FileModel file = uploadService.UploadFile(stream, filename);
return file.Id.ToString();
}
I'm trying to chance the request to "public async Task< string> Post(){ }
If the method was using a multipart form on the page instead of XmlHttpRequest I would have used "await Request.Content.ReadAsMultipartAsync(provider)" but this doesn't seem to be populated at the time I need it.
So what is the correct was to handle and an Async call from XmlHttpRequest on a Web API in order to record progress during the request with XHR's progress event?
I have looked at a great deal of pages so far to find a solution but this is the page I have used primarily.
http://robertnyman.com/html5/fileapi-upload/fileapi-upload.html
Thanks for any help
Oliver
It looks like someone else had the same question with you and got an answer yet. please have a look at ASP.NET MVC 4 Web Api ajax file upload.
And here is an example from microsoft http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2.
I combined the two above solution together and worked for me (just adjust a little bit)
one line change in Javascritp
xhr.open("post", "api/upload", true);
Save the file using stream
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var fileName = Path.Combine(root, Request.Headers.GetValues("X-File-Name").First());
try
{
var writer = new StreamWriter(fileName);
await Request.Content.CopyToAsync(writer.BaseStream);
writer.Close();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}

Spring MVC to open PDF as the view

Which is the appropriate View class to render existing PDF? AbstractView?
I am fetching PDF via a webservice ,so I'm not looking to subclass AbstractPdfView to render PDF.
I'd like to stay with the Spring controller classes which return a ModelAndView which means writing my own subclass of AbstractView to just write the PDF to a ServletOutputStream. Any other built in support available in Spring MVC?
Thanks
I agree with #Biju Kunjummen's answer but using iText would be also nice to generate the PDF.
here is the code snippet of the controller method.
#RequestMapping(value = "/common/reportgenerator/generatePDF")
public void generatePdf(HttpServletRequest req,HttpServletResponse res)
{
res.setContentType("text/html;charset=UTF-8");
ServletOutputStream outStream=null;
try
{
String calledFrom = req.getHeader("referer");
calledFrom=req.getRequestURL().substring(0,req.getRequestURL().lastIndexOf("/"))+"/ReportGenerator.egp";
calledFrom += "?isPdf=yes&"+req.getQueryString();
System.out.println(calledFrom+"?isPdf=yes&"+req.getQueryString());
InputStream input = new URL(calledFrom).openStream();
StringWriter writer = new StringWriter();
CopyUtils.copy(input, writer);
//System.out.println(writer.toString());
res.setContentType("application/pdf");
res.setHeader("Content-Disposition", "attachment;filename=report.pdf");
outStream = res.getOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(calledFrom);
renderer.layout();
renderer.createPDF(outStream);
}
catch (Exception e)
{
new AbcException(e,exceptionHandlerService);
}
finally
{
try
{
outStream.flush();
outStream.close();
}
catch(Exception ex)
{
new AbcException(ex,exceptionHandlerService);
}
}
}
Hope this helps you. Cheers.
I think the best way is to simply stream it out using HttpServletResponse:
OutputStream out = response.getOutputStream();
out.write(..); //buffer and write..
There is no such class.
You have to manually write that file.
Please see answer here:
Display the PDF file stored on the webserver on Browser new window using Spring MVC
I have changed that code to:
// get absolute path of the application
ServletContext context = request.getSession().getServletContext();
String appPath = context.getRealPath("/");
// construct the complete absolute path of the file
String fullPath = appPath + "WEB-INF/pdfs/201507.pdf";
Also, see the answer for not downloading the pdf
and putting the inputStream in the finally block.

ASP.NET Return image from .aspx link

Is it possible to output an image (or any file type) to a download link when a user clicks on a link from another ASP.NET page?
I have the file name and byte[].
Get File
...where getfile returns the file instead of going to the getfile.aspx page.
You would want .ashx for that really ;)
public class ImageHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext ctx)
{
var myImage = GetImageSomeHow();
ctx.Response.ContentType = "image/png";
ctx.Response.OutputStream.Write(myImage);
}
}
How to Create Text Image on the fly with ASP.NET
Something like this:
string Path = Server.MapPath(Request.ApplicationPath + "\image.jpg");
Bitmap bmp = CreateThumbnail(Path,Size,Size);
Response.ContentType = "image/jpeg";
bmp.Save(Response.OutputStream,System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
Here is how I have done this in the past:
Response.Clear();
Response.Buffer = true;
Response.AddHeader("Content-Disposition", string.Format("inline;filename=\"{0}.pdf\"",Guid.NewGuid()));
Response.ContentType = #"application/pdf";
Response.WriteFile(path);
Yeah, you have to clear the response completely and replace it with the image byte data as a string, and you need to make sure to set the response header for content-type according to the type of image
Yes, this is possible. There are two parts of the Response object you need to set: the Content-Type and the HTTP Header. The MSDN documentation has the details on the response object but the main concept is pretty simple. Just set the code to something like this (for a Word doc).
Response.ContentType="application/ms-word";
Response.AddHeader("content-disposition", "attachment; filename=download.doc");
There is a more complete example here
the codebehind code for getfile.aspx has to have a content-type and the browser will know that it is an image or a unknown file and will let you save it.
In asp.net you can set the ContentType by using the Response object, i.e.
Response.ContentType = "image/GIF"
Here you have a tutorial for dynamically generated image
ashx...
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
string path = ".....jpg";
byte[] imgBytes = File.ReadAllBytes(path);
if (imgBytes.Length > 0)
{
ctx.Response.ContentType = "image/jpeg";
ctx.Response.BinaryWrite(imgBytes);
}
}
public bool IsReusable
{
get {return false;}
}
}

Resources