File does not begin with '%PDF-' when displaying PDF within the browser - c#

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.

Related

Delete file after returning it on an API request

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.

Serve file in byte[] as URL

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">

ZipArchive serves up invalid file on live server

I am using ZipArchive with in a handler to serve to a user using memory stream and a web handler. Locally this was working until I uploaded the application to a live site.
Here is my code.
using (ZipArchive newArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
newArchive.CreateEntryFromFile(fileName, Path.GetFileName(fileName));
if (File.Exists(acRefFile))
{
newArchive.CreateEntryFromFile(acRefFile,
newACRefName + Path.GetExtension(acRefFile));
}
else
{
SystemLogManager sysLogMgr = new SystemLogManager();
sysLogMgr.AddErrorMessage(acRefFile, "File not found");
}
if (File.Exists(exRefFile))
{
newArchive.CreateEntryFromFile(exRefFile,
newExRefName + Path.GetExtension(exRefFile));
}
else
{
SystemLogManager sysLogMgr = new SystemLogManager();
sysLogMgr.AddErrorMessage(exRefFile, "File Not Found");
}
if (File.Exists(exRef2File))
{
newArchive.CreateEntryFromFile(exRef2File,
newExRef2Name + Path.GetExtension(exRef2File));
}
}
memoryStream.Position = 0;
byte[] bytes = memoryStream.GetBuffer();
context.Response.Buffer = true;
context.Response.Clear();
context.Response.ContentType = "application/zip";
context.Response.AddHeader("content-disposition",
string.Format("attachment; filename =app_{0}_{1}.zip", appForm.Cand_sno,
appForm.App_year));
context.Response.BinaryWrite(bytes.ToArray());
context.Response.Flush();
And the following image shows the downloaded zip file and the error generated.
So is there anything in code that could be wrong or something I could try server side?
Update 1:
Based on the comments received I tried adding the zip file directly onto the server. Same issue occurs as in the zip is 'corrupted'.
Update 2:
Further investigations I have now discovered that the zip file opens up when using 7zip but not standard windows extract. When right click extract all the message states the zip is empty.
Thanks
So the fix for this question was simply to change the byte[] bytes = MemoryStream.GetBuffer(); to byte[] bytes = MemoryStream.ToArray(); What this does is only get the used bytes not the extra bytes the buffer adds.
I use ZipFile class and the result is never corrupted.
Can you try this?
ZipFile.CreateFromDirectory("C:\somefolder", "C:\someotherfolder\somefile.zip");

Generic handler file download does not start

I'm trying to start downloading files from server, now just with some hardcoded values for files which exists but for some reason the download does not start and no error is thrown.
This is the code I have:
public void ProcessRequest(HttpContext context)
{
string destPath = context.Server.MapPath("~/Attachments/cover.txt");
// Check to see if file exist
FileInfo fi = new FileInfo(destPath);
if (fi.Exists)
{
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AppendHeader("Content-Length", fi.Length.ToString());
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=" + "cover.txt");
HttpContext.Current.Response.BinaryWrite(ReadByteArryFromFile(destPath));
HttpContext.Current.Response.End();
}
}
public bool IsReusable
{
get
{
return false;
}
}
private byte[] ReadByteArryFromFile(string destPath)
{
byte[] buff = null;
FileStream fs = new FileStream(destPath, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
long numBytes = new FileInfo(destPath).Length;
buff = br.ReadBytes((int)numBytes);
return buff;
}
I'm stepping in the code and no problem is occurring but as well no file download popup is shown in the browser.
Do you see anything wrong?
I believe the issue your having is that your calling HttpContext.Current. Since your utilizing a Generic Handler File I believe you'll want to utilize the context parameter being passed to your method signature. An example would be:
public void ProcessRequest (HttpContext context)
{
// Build Document and Zip:
BuildAndZipDocument();
// Context:
context.Response.ContentType = "application/zip";
context.Response.AddHeader("content-disposition", "filename="Commodity.zip");
zip.Save(context.Response.OutputStream);
// Close:
context.Response.End();
}
I believe if you utilize context, rather than HttpContext.Current it will resolve your issue.
NEVER use "application/zip" in your Content-Type header for a ZIP file. NEVER! Confirmed bug is in IE6 which will corrupt the download.
If you want the most universal behavior for a binary file across the most browsers past and present ALWAYS use "application/octet-stream" just like you see it used everywhere else!!
header('Content-Type: application/octet-stream');
This overcomes the IE6 bug, assuming you care. Nonetheless, you achieve nothing by switching from application/octet-stream to application/zip, so you might as well stop wasting your time on that one and keep it application/octet-stream.

How to make sure my created filedownload is UTF-8? (and not UTF-8 without BOM)

i've made a download function to download messages to a CSV file (code is below).
Now when i open it up in notepad or notepad++ i see this:
é NY ø ╬ ║► ░ ê ö
(and that is what is in the database btw)
Now, when i open it up in Ms-Excel it shows this:
é NY ø ╬ ║► ░ ê ö
When i open it up in notepad++, it says it's encoded in 'UTF8 without BOM'.
When i encode it (in notepad++) to UTF-8, all goes well (that is, Excel shows the right chars too)
But how can i make sure that the file i create from my code is UTF-8?
This is my code:
public ActionResult DownloadPersonalMessages()
{
StringBuilder myCsv = new StringBuilder();
myCsv.Append(new DownloadService().GetPersonalMessages());
this.Response.ContentType = "text/csv";
Response.AddHeader("content-disposition", "attachment; filename=PersonalMessages.csv");
Response.ContentEncoding = Encoding.UTF8;
Response.Write(myCsv.ToString());
Response.Flush();
Response.HeaderEncoding = Encoding.UTF8;
return Content("");
}
Edit:
my function now returns a ByteArray with this conversion
UTF8Encoding encoding = new UTF8Encoding();
return encoding.GetBytes(str);
and my download is now this:
Response.AddHeader("Content-Disposition", "attachment; filename=PersonalMessages.csv");
return File(new DownloadService().GetPersonalMessages(), "text/csv");
Zareth's answer helped the OP, but it didn't actually answer the question. Here's the correct solution, from this other post:
public ActionResult Download()
{
var data = Encoding.UTF8.GetBytes("some data");
var result = Encoding.UTF8.GetPreamble().Concat(data).ToArray();
return File(result, "application/csv", "foo.csv");
}
The byte-order mark (while not technically required for UTF8) clues certain programs (e.g. Excel >2007) in to the fact that you're using UTF8. You have to manually include it via the GetPreamble() method.
You might want to try using the UTF8Encoding class. The constructor has a parameter that determines if it should provide the BOM or not. You'll probably have to use the GetBytes-method and write the string as a series of bytes in the response, and not convert it back into a .net string object.
You could simplify your code a little:
public ActionResult DownloadPersonalMessages()
{
StringBuilder myCsv = new StringBuilder();
myCsv.Append(new DownloadService().GetPersonalMessages());
Response.AddHeader("Content-Disposition", "attachment; filename=PersonalMessages.csv");
return File(Encoding.UTF8.GetBytes(myCsv.ToString()), "text/csv");
}
As far as the UTF-8 encoding is concerned I am afraid the problem might be in this GetPersonalMessages method. You might need to return a stream or byte array which you could directly return as file.

Categories

Resources