Cannot open a PDF on iPad from ASP.NET - asp.net

I am downloading a PDF (and others types but lets focus on PDF) through an IIS ASP.NET web application. The download works on every other platform except Safari on iPad and iPhone 4S. I know that iOS does not support document downloads, but Safari does not open the PDF either. Clicking the link has no response on the devices. I have tried a couple of solutions listed below (take out/replace headers mostly):
http://nilangshah.wordpress.com/2007/05/28/successfully-stream-a-pdf-to-browser-through-https/ and
PHP: Download file script not working on iPad
I can see no errors on the device other than a "expected MIME" type but I also see that in the desktop browser versions and it doesn't stop the download. I am running through a proxy and can see the device receives a 200 response with the proper headers. I have successfully opened a PDF from other sites with the device.
I am just getting started reacquainted with ASP and iOS so any debug insights would also be appreciated.
Here is what the code looks like:
context.Response.Buffer = false;
context.Response.Clear();
context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.ContentType = "application/pdf";
string fileName = "thefile.pdf";
System.Web.HttpBrowserCapabilities browser = context.Request.Browser;
//I have tried with and without all the possbilities in the condition below
if (!browser.Browser.Equals("iPad"))
{
if (isDownload || viewers.Length == 0)
context.Response.AppendHeader("Content-Disposition",
string.Format("attachment; filename=\"{0}\"", fileName));
else
context.Response.AppendHeader("Content-Disposition", "filename=" + fileName);
}else
{
//context.Response.AppendHeader("Content-Disposition", string.Format("inline; filename=\"{0}\"", fileName));
}
FileStream iStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
long fileSize = iStream.Length;
long fileLengthToRead = fileSize;
int chunckSize = 10000;
byte[] buffer = new byte[chunckSize];
context.Response.AppendHeader("Content-Length", fileSize.ToString());
context.Response.AppendHeader("X-Content-Type-Options", "nosniff");
try
{
while (fileLengthToRead > 0 && context.Response.IsClientConnected)
{
int read = iStream.Read(buffer, 0, chunckSize);
context.Response.OutputStream.Write(buffer, 0, read);
context.Response.Flush();
fileLengthToRead = fileLengthToRead - read;
}
}
catch (HttpException) { }
finally
{
iStream.Close();
iStream.Dispose();
}
break;
And here is what the headers look like:
GET http://sample.com/sample.pdf HTTP/1.1
Host: sample.com
Accept-Encoding: gzip, deflate
Accept-Language: en-us
User-Agent: Mozilla/5.0 (iPad; U; CPU OS 4_3_4 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8K2 Safari/6533.18.5
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Referer: http://sample.com/sample.aspx
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 218882
Content-Type: application/pdf
Server: Microsoft-IIS/7.5
X-Content-Type-Options: nosniff
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 01 Aug 2012 15:41:06 GMT

Related

Download of xls working in IE/Firefox but named as aspx page in Chrome

I'm having an issue with Chrome renaming an export file to the default of the page name where the export is being initiated from. I've gone through all related forum posts I could find and have tried all suggestions - I'm not seeing any recent posts within the past couple of years.
My code for the export (before any modification attempts) is as follows:
public static void ExportToSpreadsheet(object items, string name)
{
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ClearHeaders();
response.Charset = "";
response.ContentType = "application/vnd.ms-excel";
response.AddHeader("Context-Disposition", "attachment;filename=\"" + name + "\"");
using (StringWriter sw = new StringWriter())
{
using (System.Web.UI.HtmlTextWriter htw = new System.Web.UI.HtmlTextWriter(sw))
{
System.Web.UI.WebControls.DataGrid dg = new System.Web.UI.WebControls.DataGrid();
dg.DataSource = items;
dg.DataBind();
dg.RenderControl(htw);
response.Write(sw.ToString());
response.End();
}
}
}
The above code works flawlessly in IE/Firefox. The resulting result headers received (per Chrome net-internals) is:
Cache-Control: private
Content-Type: application/vnd.ms-excel
Server: Microsoft-IIS/8.0
Context-Disposition: attachment;filename="DataExport.xls"
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcTXlfRGF0YVxBVFNNXERldjQwXHBvcnRhbFxDR0lfQXV0b21hdGlvbl9GcmFtZXdvcmtcQXV0b21hdGlvbl9EZWNrLmFzcHg=?=
X-Powered-By: ASP.NET
Date: Fri, 05 Dec 2014 19:37:05 GMT
Content-Length: 86493
I've tried several updates that includes hard coding the filename to "test.xls", clearing content of the response, setting buffer to true, setting CacheControl and Pragma to no-cache, and overriding VerifyRenderingInServerForm() in the page's code-behind. The response headers after applying all these changes are as follows:
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/vnd.ms-excel
Expires: -1
Server: Microsoft-IIS/8.0
Context-Disposition: attachment; filename="test.xls"
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcTXlfRGF0YVxBVFNNXERldjQwXHBvcnRhbFxDR0lfQXV0b21hdGlvbl9GcmFtZXdvcmtcQXV0b21hdGlvbl9EZWNrLmFzcHg=?=
X-Powered-By: ASP.NET
Date: Fri, 05 Dec 2014 19:10:23 GMT
Content-Length: 86493
This still produces the same results where Chrome is not recognizing/accepting the filename and defaults back to the pagename as the downloaded filename.
Any recommendations for how to fix this issue would be greatly appreciated.
I'm using Chrome Version 39.0.2171.71 m

MVC 3 client caching

I am trying to make modifications to an existing CDN. What I am trying to do is create a short cache time and use conditional GETs to see if the file has been updated.
I am tearing my hair out because even though I am setting a last modified date and seeing it in the response headers, on subsequent get requests I am not seeing an If-Modified-Since header being returned. At first I thought it was my local development environment or the fact that I was using Fiddler as a proxy for testing so I deployed to a QA server. But what I am seeing in Firebug is so different than what I am doing. I see the last modified date, for some reason it is setting my cache-control to private, and I have cleared any header Output Caching and the only header IIS 7.5 is set to write is to enable Http keep-alive, so all the caching should be driven by the code.
This seemed like such a no-brainer, yet I've been adding and removing headers all day with no luck. I checked global.asax and anywhere else (I didn't write the app so I was looking for any hidden surprises and am stumped. Below is the current code and request and response headers. I have the expiration set to 30 seconds just for testing purposes. I have looked at several samples, I don't see myself doing anything different, but it simply won't work.
Response Headersview source
Cache-Control private, max-age=30
Content-Length 597353
Content-Type image/jpg
Date Tue, 03 Sep 2013 21:33:55 GMT
Expires Tue, 03 Sep 2013 21:34:25 GMT
Last-Modified Tue, 03 Sep 2013 21:33:55 GMT
Server Microsoft-IIS/7.5
X-AspNet-Version 4.0.30319
X-AspNetMvc-Version 3.0
X-Powered-By ASP.NET
Request Headersview source
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Connection keep-alive
Cookie __utma=1.759556114.1354835397.1377631052.1377732484.36; __utmz=1.1354835397.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Host hqat4app1
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetLastModified(DateTime.Now);
return new FileContentResult(fileContents, contentType);
The relevant code is:
public ActionResult Resize(int id, int size, bool grayscale)
{
_logger.Debug(() => string.Format("Resize {0} {1} {2}", id, size, grayscale));
string imageFileName = null;
if (id > 0)
using (new UnitOfWorkScope())
imageFileName = RepositoryFactory.CreateReadOnly<Image>().Where(o => o.Id == id).Select(o => o.FileName).SingleOrDefault();
CacheImageSize(id, size);
if (!ImageWasModified(imageFileName))
{
Response.Cache.SetExpires(DateTime.Now.AddSeconds(30));
Response.StatusCode = (int)HttpStatusCode.NotModified;
Response.Status = "304 Not Modified";
return new HttpStatusCodeResult((int)HttpStatusCode.NotModified, "Not-Modified");
}
byte[] fileContents;
if (ShouldReturnDefaultImage(imageFileName))
fileContents = GetDefaultImageContents(size, grayscale);
else
{
bool foundImageFile;
fileContents = GetImageContents(id, size, grayscale, imageFileName, out foundImageFile);
if (!foundImageFile)
{
// No file found, clear cache, disable output cache
//ClearOutputAndRuntimeCacheForImage(id, grayscale);
//Response.DisableKernelCache();
}
}
string contentType = GetBestContentType(imageFileName);
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetLastModified(DateTime.Now);
return new FileContentResult(fileContents, contentType);
}
private bool ImageWasModified(string fileName)
{
bool foundImageFile;
string filePath = GetFileOrDefaultPath(fileName, out foundImageFile);
if (foundImageFile)
{
string header = Request.Headers["If-Modified-Since"];
if(!string.IsNullOrEmpty(header))
{
DateTime isModifiedSince;
if (DateTime.TryParse(header, out isModifiedSince))
{
return isModifiedSince < System.IO.File.GetLastWriteTime(filePath);
}
}
}
return true;
}

Help reading JSON from HttpContext.InputStream

I have created a HttpModule to capture requests for auditing purposes.
For Ajax requests to a web method I would like to also log the JSON data associated with the request.
E.g Request
POST /MyPage.aspx/AddRecord HTTP/1.1
x-requested-with: XMLHttpRequest
Accept-Language: en-gb
Referer: http://fiddlerlocal:5000/AddRecord.aspx
Accept: application/json, text/javascript, /
Content-Type: application/json; charset=utf-8
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Host: fiddlerlocal:5000
Content-Length: 287
Connection: Keep-Alive
Pragma: no-cache
Cookie: .....
{"id":"282aa3b5-b55f-431c-916e-60433fdb61c0","date":"8-6-2010"}
I have tried a variety of methods to read the JSON ({"id":"282aa3b5-b55f-431c-916e-60433fdb61c0","date":"8-6-2010"}) from the HttpContext.InputStream.
Example 1:
StreamReader reader = new StreamReader(request.InputStream);
string encodedString = reader.ReadToEnd(); -- ReadToEnd returns an empty string
Example 2:
using (MemoryStream ms = new MemoryStream())
{
byte[] buffer = new byte[request.ContentLength];
request.InputStream.Read(buffer, 0, request.ContentLength);
ms.Write(buffer, 0, request.ContentLength); -- The byte array contains the correct number of bytes but each byte has a value of 0 - encoded some how?
return Convert.ToBase64String(ms.ToArray()); -- doesn't do anything
return Encoding.UTF8.GetString(ms.ToArray()); -- doesn't do anything
}
How can I successfully extract the data from HttpContext.InputStream?
Thanks in advance.
I needed to reset the position of the stream before reading...
request.InputStream.Position = 0;
using (StreamReader inputStream = new StreamReader(request.InputStream))
{
return inputStream.ReadToEnd();
}
The stream can't be read as far as i know. You might write you own handler, then buffer the stream, by reading and writing to another stream.
To parse the JSON part you might try
System.Web.Script.Serialization.JavaScriptSerializer.DeserializeObject(string input);

Asp.Net Sending PDF to browser

I've been trying to get this aspx page to serve up a pdf. It works correctly in Firefox, but IE gives
Internet Explorer cannot download getform.aspx from SERVER_NAME
Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found.
This is the general functionality of my code. It's spread across multiple functions (this is why we're not using WriteFile - sometimes we generate the pdf on the fly), but this is generally it:
FileStream fs = File.Open(Path.Combine(PdfBasePath, "form.pdf"), FileMode.Open, FileAccess.Read);
Stream output = Response.OutputStream;
byte[] buffer = new byte[BUFFER_SIZE];
int read_count = fs.Read(buffer, 0, BUFFER_SIZE);
while (read_count > 0)
{
output.Write(buffer, 0, read_count);
read_count = fs.Read(buffer, 0, BUFFER_SIZE);
}
fs.Close();
Response.Clear();
Response.ContentType = System.Net.Mime.MediaTypeNames.Application.Pdf;
Response.AddHeader("Content-Disposition", "attachment; filename=form.pdf");
Response.Output.Flush();
Response.End();
Looking at Fiddler, the page is being fetched using this:
GET /getform.aspx?Failure=Y&r=someencryptedstring HTTP/1.1
It is being returned to the browser thus:
HTTP/1.1 200 OK
Date: Thu, 09 Apr 2009 22:08:33 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Pragma: no-cache
Content-Disposition: attachment; filename=form.pdf
Cache-Control: no-cache, no-store
Pragma: no-cache
Expires: -1
Content-Type: application/pdf
Content-Length: 628548
This is really bugging me. I'm not using SSL, otherwise this KB article would seem to apply. Anyone have any ideas?
Is the Content-Length being returned in the header actually correct for the file you're sending? I'm just comparing this to some production code we use here and it looks like we explicitly set the Content-Length header. If I recall correctly, some browsers have a problem if the header and the actual file size don't match.
Edit
The question author found that changing the Content-Disposition header to application/download instead of application/pdf seems to work around the problem.

VS Builtin web server sends images as octet-stream

I am debugging an ASP.NET website which has a lot of javascripts and images using Visual Studio 2008 development web server.
One of the many scripts try to create an <img> tag on the fly and supply it with a proper src attribute. However, none of the images are loaded and instead alt text are displayed in Firefox, IE and Opera.
Digging further, I copied one of the image link and then paste it in Firefox's address bar and this is what comes up in live headers window:
GET /images/nav/zoomin.png HTTP/1.1
Host: localhost:7777
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
HTTP/1.x 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Wed, 25 Feb 2009 16:59:23 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: application/octet-stream
Content-Length: 292
Connection: Close
The problematic part is the Content-Type header which is somehow set to "application/octet-stream" forcing a download operation instead of showing normally inside the <img> tag.
I am quiet sure that it isn't the javascript that is the problem, because it is code that has been copied verbatim from another application that worked just fine.
I believe I might have misconfigured something somewhere. But I could be wrong, so here's the code that create the HTML tag:
var zin = document.createElement("img");
zin = $Img.Png(zin, Settings.ImageHost + "zoomin.png");
zin.style.top = (this.y + zoomPaddingY) + "px";
zin.style.left = (this.x + zoomPaddingX) + "px";
zin.style.position = "absolute";
$Img.Swap(zin, Settings.ImageHost + "zoomin.png", Settings.ImageHost + "zoomin_active.png");
zin.alt = zin.title = "zoom in";
zin.style.cursor = this.hand;
$Evt.addListener(zin, "click", this.zoomIn, this, true);
// long long scroll ...
controlDiv.appendChild(zin);
The $Img.Png part is working fine for other PNG images, so it shouldn't be the source of the problem.
What did I did wrong?!?
Thanks for any help!
It's already midnight here... and I'm still working on this little app...
Are you using a GenericHandler that renders the image?
It would seem like an easy choice to do so.
Eg.
public class RenderImage : IHttpHandler, IReadOnlySessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/png";
context.Response.Clear();
// TODO: Write image data
Bitmap bitmap = ...
bitmap.Save(Response.OutputStream,ImageFormat.Png);
context.Response.End();
}
public bool IsReusable { get { return false; } }
}

Resources