I am working on a serverside component of a webapp which should display images stored in database.
I am trying to find a way to transform a byte array or a stream to a valid URL for the HTML img tag.
The byte[] contains the entire file including headers.
I have searched for a solution but I kept finding the reverse problem of saving to filestream from a url.
Is there a way to serve the file via some kind of a dynamically generated url or do I have to create a physical copy of the file to link to?
You can convert the Byte Array to a Base64 image.
public string getBase64Image(byte[] myImage)
{
if (myImage!= null)
{
return "data:image/jpeg;base64," + Convert.ToBase64String(myImage);
}
else
{
return string.Empty;
}
}
Your image tag will look like this: <img src="data:image/jpeg;base64,/9j/4AAQSkZJRgA...">
Or for larger images (and other file types) it's better to use a Generic Handler
public void ProcessRequest(HttpContext context)
{
//check if the querystring 'id' exists
if (context.Request.QueryString["id"] != null)
{
string idnr = context.Request.QueryString["id"].ToString();
//check if the id falls withing length parameters
if (idnr.Length > 0 && idnr.Length < 40)
{
//get the data from the db or other source
byte[] bin = getMyDataFromDB();
//clear the headers
context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.Clear();
context.Response.Buffer = true;
//if you do not want the images to be cached by the browser remove these 3 lines
context.Response.Cache.SetExpires(DateTime.Now.AddMonths(1));
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetValidUntilExpires(false);
//set the content type and headers
context.Response.ContentType = "image/jpeg";
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"myImage.jpg\"");
context.Response.AddHeader("content-Length", bin.Length.ToString());
//write the byte array
context.Response.OutputStream.Write(bin, 0, bin.Length);
//cleanup
context.Response.Flush();
context.Response.Close();
context.Response.End();
}
}
}
Your image tag will look like this: <img src="/Handler1.ashx?id=AB-1234">
Related
I get a request with which I create a file and return it to the client.
After the file is sent I want it deleted.
Since I get many request, files are big and memory is scarce, I don't want to buffer it in memory to send it.
The only method I got to work without buffering the whole file in memory was:
Response.TransmitFile(filepath)
The problem with this is that it does it asynchronously, so if I delete it after that call the file download is interrupted.
I tried calling Flush, adding the delete on a finally block but neither of those worked. I thought of inheriting HttpResponse to try modify TransmitFile, but it's a sealed class. I tried to use HttpResponse.ClientDisconnectedToken but either I don't understand how to use it correctly or it isn't working in this case.
How can I achieve this? Is there a better method than calling HttpResponse's TransmitFile? Always taking into account that this is an API, files can't be broken into different requests and that it doesn't load the full file in memory.
I'm not sure if it could help somehow, but my controller is inheriting from AbpApiController.
You create the file in a temp folder, just create a job to remove all files based on date/time. Maybe give the user 4 hours to download the file.
An implementation of TransmitFileWithLimit, It can be improve in many ways, but it works
Extension for HttpResponse
public static class HttpResponseExtensions
{
public static void TransmitFileWithLimit(this HttpResponse response, string path, int limit)
{
var buffer = new byte[limit];
var offset = 0;
response.ClearContent();
response.Clear();
response.ContentType = "text/plain";
using (var fileStream = File.OpenRead(path))
{
var lengthStream = fileStream.Length;
while (offset < lengthStream)
{
var lengthBytes = fileStream.Read(buffer, 0, limit);
var chars = System.Text.Encoding.ASCII.GetString(buffer, 0, lengthBytes).ToCharArray();
response.Write(chars, 0, lengthBytes);
offset += lengthBytes;
}
}
response.Flush();
response.End();
}
}
Inside Controller
public void Get()
{
var path = #"C:\temporal\bigfile.mp4";
var response = HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.AddHeader("Content-Disposition", "inline; filename=" + HttpUtility.UrlPathEncode(path));
response.ContentType = "text/plain";
response.AddHeader("Content-Length", new FileInfo(path).Length.ToString());
response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
TransmitFileWithLimit(path, 10000);
File.Delete(path);
}
What you can do is to create a byte array from your file and then delete the file.
So, once it is created, you store it in memory, then you delete the file and return the byte array.
Check this answer to see an example.
I hope this helps.
How to display images in different formats?
I tried this.
context.Response.ContentType = "image/jpeg";
This will display only jpeg images.But I need to display images in different formats.
Edit :
I understand from comments that your question is :
how to set the src of an ASP.Net image to be the base64 encoded data ?
Or
How display images that come from database in different format in an asp.net image?
Assuming that you get image bytes from database and you have an img tag
<img id= "img" runat= "server" />
You can create an extension method for byte[] like this
// Add a new csharp file to your project
// Add below code to it
public static class DrawingExtensions
{
public static string ConvertToBase64ImageFormat ( this byte[] b, ImageFormats f)
{
// you can find other formats image mimeType and add all you need
string mt = system.Net.Mime.MediaTypeNames.Image.Tiff;
string i = Convert.ToBase64String(b);
return string.Format("data:{0};base64,{1}",mt,i);
}
}
public enum ImageFormats
{
Jpeg
Png ,
// And other formats
}
On your webform page use method like this
public partial class DisplayImagePage:Page
{
public void ibDisplayImg_click(object sender,EventArgs e)
{
// read image bytes from database
Byte[] i= ReadImageFromDatabase();
img.Src = i.ConvertToBase64ImageFormat( ImageFormats.Png);
}
}
If you want mime type for other kind of images they available with
System.Web.MimeMapping
And use like this
string mimeType = MimeMapping.GetMimeMapping(fileName);
context.Response.ContentType = mimeType;
More information :
https://msdn.microsoft.com/en-us/library/ms525208(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/system.web.mimemapping.getmimemapping.aspx
So here are the details:
Firefox: update 12
AdobeReader: 11
Input: Convert.FromBase64String(string)=> byte[]
Task: to display the pdf within the browser
PDF is stored in a database.
I've read and tried a lot of possible solutions and fixes for this error. But I got no joy.
Is it possible to know if the converted string to byte[] is corrupted? And is it possible for the value being converted to byte[] to be damaged in the process?
The value of the pdfFile, data type byte[], is from a web service.
Here is the generic handler that I made:
public partial class ProcessPDFRequest : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
byte[] currentBillPDF = proxy.GetPdf(refNum, date);
using (MemoryStream ms = new MemoryStream(currentBillPDF))
{
context.Response.Clear();
context.Response.ContentType = "application/pdf";
if(isInline!="true")
context.Response.AddHeader("content-disposition", "attachment;filename=PDF_CurrentBill.pdf");
else
context.Response.AddHeader("content-disposition", "inline;filename=PDF_CurrentBill.pdf");
context.Response.Buffer = true;
ms.WriteTo(context.Response.OutputStream);
context.Response.End(); ;
}
EDIT
I use context.Response.Flush(); context.Response.End();
PDF was successfully displayed when I created another web application. However when I used the converted string to byte[] in the original solution, the error stated in the title is persisting. I already checked the bytes and compared it in notepad, both them showed %PDF-
Is there something I'm missing? the original solution is a sharepoint web application.
I have to serve large files (200-800MB) to the client from my controller. I tested FileStreamResult, but this class buffered the whole file in memory. This behavior is not good enough for my project.
Further i testest the approach from here: http://support.microsoft.com/kb/812406. Concerning the memory, this looks pretty good -but the files are not completly downloaded on the client (the original file is 210222 KB, the downloaded ones are 209551 to 209776). This means there is about 0.5 MB lost (which concequently causes the files to be broken).
Has somebody an idea? Whats the best way to do this anyway? Im grateful for everything.
Just for users in the future, the link pointed to the following code:
System.IO.Stream iStream = null;
// Buffer to read 10K bytes in chunk:
byte[] buffer = new Byte[10000];
// Length of the file:
int length;
// Total bytes to read:
long dataToRead;
// Identify the file to download including its path.
string filepath = "DownloadFileName";
// Identify the file name.
string filename = System.IO.Path.GetFileName(filepath);
try
{
// Open the file.
iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open,
System.IO.FileAccess.Read,System.IO.FileShare.Read);
// Total bytes to read:
dataToRead = iStream.Length;
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
// Read the bytes.
while (dataToRead > 0)
{
// Verify that the client is connected.
if (Response.IsClientConnected)
{
// Read the data in buffer.
length = iStream.Read(buffer, 0, 10000);
// Write the data to the current output stream.
Response.OutputStream.Write(buffer, 0, length);
// Flush the data to the HTML output.
Response.Flush();
buffer= new Byte[10000];
dataToRead = dataToRead - length;
}
else
{
//prevent infinite loop if user disconnects
dataToRead = -1;
}
}
}
catch (Exception ex)
{
// Trap the error, if any.
Response.Write("Error : " + ex.Message);
}
finally
{
if (iStream != null)
{
//Close the file.
iStream.Close();
}
Response.Close();
}
Update
This is my action:
public DownloadResult TransferTest()
{
string fullFilePath = #"C:\ws\Test\Test\Templates\example.pdf";
return new DownloadResult(fullFilePath);
}
I simply call the action directly from my browser (http://xxx.xxx/Other/TransferTest).
The code basically looks sound - you are more-or-less correctly handling the return value from Read (if I was being picky, I would say check it for <=0, but this would not be an expected behavior since you probably have a lock on the file).
The only thing that occurs is: try adding a:
Response.OutputStream.Flush();
and perhaps:
Response.OutputStream.Close();
to make sure that the output stream is flushed.
Greetings!
I'm creating a web form prototype (ImageLaoder.aspx) that will return an image so that it may be used like this simple example other Web Forms/web pages:
<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />
So far, it loads JPGs with no problems, however GIFs look "grainy" compared to the orignals and BMPs and PNGs result in the following exception:
System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+
My code thus far looks like this:
protected void Page_Load(object sender, EventArgs e)
{
string l_filePath = Request.QueryString["i"];
System.Drawing.Image l_image = GetImage(l_filePath);
if (l_image != null)
{
System.Drawing.Imaging.ImageFormat l_imageFormat = DetermineImageFormat(l_filePath);
WriteImageAsReponse(l_image, l_imageFormat);
}
}
private System.Drawing.Image GetImage(string filePath)
{
WebClient l_WebClient = new WebClient();
byte[] l_imageBytes = l_WebClient.DownloadData(filePath);
System.Drawing.Image l_image = null;
using (MemoryStream l_MemStream = new MemoryStream(l_imageBytes, 0, l_imageBytes.Length))
{
l_MemStream.Write(l_imageBytes, 0, l_imageBytes.Length);
l_image = System.Drawing.Image.FromStream(l_MemStream, true);
l_MemStream.Close();
}
return l_image;
}
private System.Drawing.Imaging.ImageFormat DetermineImageFormat(string filePath)
{
if (filePath.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
return System.Drawing.Imaging.ImageFormat.Jpeg;
else if (filePath.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
return System.Drawing.Imaging.ImageFormat.Gif;
else if (filePath.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
return System.Drawing.Imaging.ImageFormat.Png;
else
return System.Drawing.Imaging.ImageFormat.Bmp;
}
private void WriteImageAsReponse(System.Drawing.Image image, System.Drawing.Imaging.ImageFormat imageFormat)
{
if (image == null)
return;
System.Drawing.Bitmap l_outputBitMap = new Bitmap(image);
if (imageFormat == System.Drawing.Imaging.ImageFormat.Jpeg)
Response.ContentType = "image/jpg";
else if (imageFormat == System.Drawing.Imaging.ImageFormat.Gif)
Response.ContentType = "image/gif";
else if (imageFormat == System.Drawing.Imaging.ImageFormat.Png)
Response.ContentType = "image/png";
else
Response.ContentType = "image/bmp";
l_outputBitMap.Save(Response.OutputStream, imageFormat);
}
Any ideas why GIFs are grainy and PNGs and BMPs cause exceptions?
A few points about your GetImage method:
When you use Image.FromStream you shouldn't close (or dispose) the stream
If you're calling Dispose on a stream (with the using statement) you don't need to call Close
You're writing to the stream, but then not "rewinding it" so l_image doesn't actually get any data as far as I can see (unless Image.FromStream resets the position itself). (It could be that the gif/jpg decoders rewind the stream but bmp/png don't, hence the error.)
Why don't you just use the MemoryStream constructor which takes a byte array?
In short, I believe your GetImage method can be replaced with:
private Image GetImage(string filePath)
{
WebClient l_WebClient = new WebClient();
byte[] l_imageBytes = l_WebClient.DownloadData(filePath);
MemoryStream l_stream = new MemoryStream(l_imageBytes);
return Image.FromStream(l_stream);
}
Now, more importantly - why are you loading the image at all? Why don't you just serve the file itself as a response, setting the content type as you're already doing - or possibly just based on the extension? In other words, all of your code would become:
protected void Page_Load(object sender, EventArgs e)
{
string filePath = Request.QueryString["i"];
string extension = l_filePath.Substring(l_filePath.LastIndexOf('.') + 1);
Response.ContentType = "image/" + extension;
byte[] data = new WebClient.DownloadData(filePath);
Response.OutputStream.Write(data, 0, data.Length);
Response.End();
}
A bit more error handling (including "is this a reasonable extension?") would be nice, but other than that I think it's okay. The only benefit of actually loading the image yourself is that you get to validate that it really is an image rather than a virus or something like that.
EDIT: Just out of interest, do you have a good reason why you'd want image requests to go through your server? Why would the web page author write:
<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />
instead of
<img src="http://images.mydomain.com/img/a.jpg" />
There are some reasons why it might be useful, but in many cases it's just a waste.