Issue returning images over web api - c#

I am having a weird issue returning an image via web api, I do this in several places in my application without fail but this one is causing me issues, any help would be greatly appreciates.
This works
Bitmap bitmap = getImage();
MemoryStream bitmapStream = new MemoryStream();
bitmap.Save("C:\\test.png", System.Drawing.Imaging.ImageFormat.Png);
if (bitmap != null)
{
if (Request.Headers.IfModifiedSince.HasValue)
{
// The file has not been modified since the browser cached it.
return new HttpResponseMessage(HttpStatusCode.NotModified);
}
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new FileStream("C:\\test.png", FileMode.Open));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return response;
}
else
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
while this does not
Bitmap bitmap = getImage();
MemoryStream bitmapStream = new MemoryStream();
bitmap.Save(bitmapStream, System.Drawing.Imaging.ImageFormat.Png);
if (bitmap != null)
{
if (Request.Headers.IfModifiedSince.HasValue)
{
// The file has not been modified since the browser cached it.
return new HttpResponseMessage(HttpStatusCode.NotModified);
}
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(bitmapStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return response;
}
else
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
I really need to do this in memory and not save a temp file to drive, but for some reason when I do it in memory the result is an empty file 0 bytes, I do try to set the content length manually but then the file doesnt download at all.

You would need to reset the position of the stream after the save happens.
bitmapStream.Position = 0;

Related

Display PDF file from C# to Angular2 view

I'm trying to display a PDF that was previously uploaded into the server. The PDF resides inside the App_Data folder. I want to fetch it using C# Web API 2 and display it in my Angular 2 frontend view.
C# code:
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest);
string filename = "somefilename.pdf";
byte[] buffer = new byte[0];
MemoryStream memStream = new MemoryStream();
if (filename != "") {
PdfReader reader = new PdfReader(Path.Combine(HttpContext.Current.Request.PhysicalApplicationPath) + "/App_Data/Uploads/PDFs/" + filename);
PdfStamper stamper = new PdfStamper(reader, memStream);
response = Request.CreateResponse(HttpStatusCode.OK);
buffer = memStream.ToArray();
response.Content = new StreamContent(new MemoryStream(buffer));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentLength = buffer.Length;
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=" + filename, out contentDisposition))
{
response.Content.Headers.ContentDisposition = contentDisposition;
}
}
return response;
Angular 2 code:
this.documentsService.getFile()
.subscribe((response: any) => {
let file = new Blob([response], { type: 'application/pdf' });
let url = URL.createObjectURL(file);
window.open(url);
});
I think my Angular code is wrong. But nevertheless, my first goal here is to fetch it from the MVC side. Right now the file gets downloaded and it's corrupted or there's not fetch correctly. It always give me a 15bytes size PDF file, so I know there's something wrong. I'm using iTextSharp in my C# backend.
Thanks in advance!
From my comment (providing an answer is easier for code):
if (filename != "") {
var fs = System.IO.File.OpenRead(filename);
_myDisposable = fs; // see further down in this answer.
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(fs);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
return response;
Edit: The code I posted above works for me. The only problem is disposing of the stream. The best way to probably do that is by creating a private IDisposable member on your controller class, and then adding a dispose override, sort of like this:
private IDisposable _myDisposable;
public override void Dispose(bool disposing)
{
if (disposing && _myDisposable != null)
_myDisposable.Dispose();
}
Please note that this assumes ASP.NET is done with the request (which seems reasonable, because it's disposing of your controller), which should be correct.

ERR_SPDY_PROTOCOL_ERROR when returning file from ASP.NET action

I have some old Web API action method which returns CSV file. It worked for long time, but recently stopped. Now it causes ERR_SPDY_PROTOCOL_ERROR.
ERR_SPDY_PROTOCOL_ERROR in Chrome is often associated with Avast security as described here. In my case however it's not caused by Avast, and other web browsers throw exceptions too.
My action method looks as follows:
[HttpGet]
[Route("csv")]
public HttpResponseMessage SomeMethod([FromUri]SomeSearchCriteria sc)
{
using (MemoryStream stream = new MemoryStream())
{
StreamWriter writer = new StreamWriter(stream, Encoding.UTF8);
string content = someLogic.SomeSearchmethod(sc);
writer.Write(content);
writer.Flush();
stream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Export.csv" };
return result;
}
}
The method is called by angular front end by simple change of window.location on button click.
Whole action method is executed properly, with no exceptions. Error is shown only by web browser.
Flushing sockets in Chrome as described here does not solve the issue.
I have tried this method in API controller and call through chrome browser, it throws net::ERR_CONNECTION_RESET
There is some issue in response filled with StreamContent, use ByteArrayContent in result content, it works perfectly.
[HttpGet]
[Route("csv")]
public HttpResponseMessage SomeMethod([FromUri]SomeSearchCriteria sc)
{
using (MemoryStream stream = new MemoryStream())
{
StreamWriter writer = new StreamWriter(stream, Encoding.UTF8);
string content = "test";
writer.Write(content);
writer.Flush();
stream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
//result.Content = new StreamContent(stream);
result.Content = new ByteArrayContent(stream.ToArray());
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Export.csv" };
return result;
}
}

REST response trying to IMG or other filetype

Ive got a question I am having a case of rest response that is always string, it suppose to download content of the file, but there can be many different files, for example PNG, now if I'm getting a string in response is it possible to convert it back to PNG at the end, I tried something like:
byte[] array = Encoding.ASCII.GetBytes(result.data); //response content
MemoryStream ms = new MemoryStream(array);
Image i = Image.FromStream(ms);
I dont think im getting base64 string from rest looks like (part of it, and if i remmebr base64 ends with 3 === and don't have any non printable chars):
�PNG\r\n\n\0\0\0\rIHDR\0\0�\0\0\0�\b\0\0\0���\0\0\0sRGB\0���\0\0\0gAMA\0\0��\v�a\0\0\0\tpHYs\0\0t\0\0t�fx\0\0\f\aIDATx^��!x�L���Jde%�yY�D�H$�d%2��S��Hd%y�<�ӹ�B�ٝ�O�mﺔ��d����\r\0���\a�(H|\0���\a�(H|\0���\a�(H|\0���\a�(H|\0���\a�(H|\0���\a�(H|\0���\a�(H|\0���\a�(H|\0���\a�(H|\0���\a�(H|\0�\"D�WU��v������r��o#\f!��y�����K�\0�'D�O�SM�\f�����\0��Dǯ�z4�R��C�7\0��+�\0�\0Q��\0�\0Q��UU���ļO ?�������!�#J�>���|D��$>\f�|D��$>\f��7X,�?�_\v]�V�^/�=��#4$�����$:��P9
Assuming you are returning base 64 string from the API response, you can do something like this
byte[] bytes = Convert.FromBase64String(result.data);
Image image;
using (MemoryStream ms = new MemoryStream(bytes))
{
image = Image.FromStream(ms);
}
return image;
Or you can save it directly to the file
string filePath = "Image.png";
File.WriteAllBytes(filePath, Convert.FromBase64String(result.data));
EDIT 1:
How are you returning data from your web API? You could do something like this to return byte array and then use this array directly to write to stream.
var result = new HttpResponseMessage(HttpStatusCode.OK);
String filePath = HostingEnvironment.MapPath("~/imagename.png");
FileStream fileStream = new FileStream(filePath, FileMode.Open);
Image image = Image.FromStream(fileStream);
MemoryStream memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Jpeg);
result.Content = new ByteArrayContent(memoryStream.ToArray());
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return result;
and on the client side you can do something like
var data = response.Content.ReadAsByteArrayAsync().Result;
Image image;
using (MemoryStream ms = new MemoryStream(data))
{
image = Image.FromStream(ms);
}
return image;

PushStreamContent and ionic.zip

My webapi method for zipping on the fly use this code
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new PushStreamContent((stream, content, arg3) =>
{
using (var zipEntry = new Ionic.Zip.ZipFile())
{
using (var ms = new MemoryStream())
{
_xmlRepository.GetInitialDataInXml(employee, ms);
zipEntry.AddEntry("content.xml", ms);
zipEntry.Save(stream); //process sleep on this line
}
}
})
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "FromPC.zip"
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
I want to
1) take data from _xmlRepository.GetInitialDataInXml
2) zip data on the fly via Ionic.Zip
3) return zipped stream as output of my WebApi action
But on this line zipEntry.Save(stream); execution process stops and don't go to next line. And method don't return anything
So why it doesnt' return me file?
When using PushStreamContent, you would need to close the stream to signal that you are done writing to the stream.
Remarks section in the documentation:
http://msdn.microsoft.com/en-us/library/jj127066(v=vs.118).aspx
The accepted answer is not correct. It is not necessary to close the stream if you want to start streaming. The streaming starts automatically (download dialog in browser) when the delegated function ends. In case of big files OutOfMemoryException is thrown, but it is handled and the streaming begins -> HttResponseStream is flushed towards the client.
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new PushStreamContent(async (outputStream, httpContext, transportContext) =>
{
using (var zipStream = new ZipOutputStream(outputStream))
{
var employeeStream = _xmlRepository.GetEmployeeStream(); // PseudoCode
zipStream.PutNextEntry("content.xml");
await employeeStream.CopyToAsync(zipStream);
outputStream.Flush();
}
});
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "FromPC.zip" };
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;

Upload image to a server from PictureBox

I have one pictureBox and a button on form1 one. When the button is clicked, it should upload the file to the server. For now I am using the below method. First save the image locally and then upload to the server:
Bitmap bmp = new Bitmap(this.form1.pictureBox1.Width, this.form1.pictureBox1.Height);
Graphics g = Graphics.FromImage(bmp);
Rectangle rect = this.form1.pictureBox1.RectangleToScreen(this.form1.pictureBox1.ClientRectangle);
g.CopyFromScreen(rect.Location, Point.Empty, this.form1.pictureBox1.Size);
g.Dispose();
bmp.Save("filename", ImageFormat.Jpeg);
And then uploading that file:
using (var f = System.IO.File.OpenRead(#"F:\filename.jpg"))
{
HttpClient client = new HttpClient();
var content = new StreamContent(f);
var mpcontent = new MultipartFormDataContent();
content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
mpcontent.Add(content);
client.PostAsync("http://domain.com/upload.php", mpcontent);
}
I can't use the Bitmap type in StreamContent. How can I stream the image from pictureBox directly instead saving it as file first?
I came up with the below code using MemoryStream, but the uploaded file size is 0 using this method. Why?
byte[] data;
using (MemoryStream m = new MemoryStream())
{
bmp.Save(m, ImageFormat.Png);
m.ToArray();
data = new byte[m.Length];
m.Write(data, 0, data.Length);
HttpClient client = new HttpClient();
var content = new StreamContent(m);
var mpcontent = new MultipartFormDataContent();
content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
mpcontent.Add(content, "file", filename + ".png");
HttpResponseMessage response = await client.PostAsync("http://domain.com/upload.php", mpcontent);
//response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
MessageBox.Show(body);
}
I am not sure if it is the correct way to do it, but I have solved it by creating a new stream and then copying the older one to it:
using (MemoryStream m = new MemoryStream())
{
m.Position = 0;
bmp.Save(m, ImageFormat.Png);
bmp.Dispose();
data = m.ToArray();
MemoryStream ms = new MemoryStream(data);
// Upload ms
}
Image returnImage = Image.FromStream(....);

Categories

Resources