My C# web application resides on the C drive.
However the application receives uploaded documents from users and saves them on the D drive.
How do I specify the path to documents on the D drive in the NavigateUrl property of an HyperLink control in the web application on the C drive.
In server-side, you could load the file inside a Stream and response this object as a byte[]
public void Page_Load(object sender, System.EventArgs e)
{
// create a FileStream from a path from local (D:, C:, E:, etc...)
FileStream file = File.OpenRead(#"d:\folder\yourfile.txt");
//Convert the stream to an array of bytes.
byte[] byteArray = file.ToArray();
// Clear all content output from the buffer stream
Response.Clear();
// Add a HTTP header to the output stream that specifies the default filename
// for the browser's download dialog
Response.AddHeader("Content-Disposition", "attachment; filename=foo.txt");
// Add a HTTP header to the output stream that contains the
// content length(File Size). This lets the browser know how much data is being transfered
Response.AddHeader("Content-Length", byteArray.Length.ToString());
// Set the HTTP MIME type of the output stream
Response.ContentType = "application/octet-stream";
// Write the data out to the client.
Response.BinaryWrite(byteArray);
}
Related
I am trying to store an uploaded file (via ajax file upload) into a LONGBLOB column of a MySQL database. Reason for a LONGBLOB, the user is allowed to upload a file up until 10MB in file size.
The first issue is that I couldn't upload just the file byte[] into the column, as it complained about an "incorrect string value".
So, I decided to do a Convert.ToBase64String of the file byte[] and store that. No problem (I hope).
My main problem is that I want to retrieve that stored blob and write it to an Outputstream in order to view the file in the browser. The file is however corrupt.
The difference that I do pick up is that the byte[] length of the original file is significantly less than the byte[] of the blob (after I retrieve and convert it back to a byte[]).
So, code wise, here is what I have attempted for the saving of the uploaded file:
byte[] fileData = null;
//file is the uploaded file
file.InputStream.Position = 0;
using (var reader = new BinaryReader(file.InputStream))
{
fileData = reader.ReadBytes(file.ContentLength);
}
var encryptedFileStream = Convert.ToBase64String(fileData);
//Storing encryptedFileStream to LONGBLOB type
Below is what I have attempted for the reading of the blob into a OutputStream:
var decryptedFileStream = Convert.FromBase64String(Convert.ToBase64String(file.FileStream)); //file.FileStream is a byte[] datatype.
WebClient client = new WebClient();
var response = ((Handler)ExecutionContext).Response;
response.Clear();
response.ContentType = file.FileExtension;
response.AppendHeader("content-disposition", (inline ? "inline" : "attachment") + "; filename=" + file.FileName);
response.AppendHeader("content-length", decryptedFileStream.Length.ToString());
response.OutputStream.Write(decryptedFileStream, 0, decryptedFileStream.Length);
I am generating some JSON content, and then GZipping that content, before returning the gzipped content to the user, from an MVC Controller Action.
The generation of the content, and gzipping, is working correctly as I can output the generated file to disk, and then I can open that file using GZip. However, when the content is returned to the browser, the content has been corrupted.
I have tried several different approaches to returning the content to the browser, such as
return File(byte[], "application/gzip");
return new FileStreamResult(stream, "application/gzip")
And also writing directly to the Response using BinaryWrite() and WriteFile() methods
No matter what I do, the file I receive in the browser is corrupt.
This code shows the manner in which I am currently trying to return the file content.
// This line writes my content byte[] array to disk. This file when opened with gzip works fine.
System.IO.File.WriteAllBytes(#"C:\temp\test.vcp", result.FileBytes);
// Writing out the byte array to the Response results in a corrupt file. I have also attempted to Response.WriteFile(#"C:\temp\test.vcp") which also results in a corrupt file.
Response.Clear();
Response.ContentType = "application/gzip";
Response.AppendHeader("Content-Disposition", cd.ToString());
Response.AddHeader("Content-Length", result.FileBytes.Length.ToString());
Response.BinaryWrite(result.FileBytes);
Response.Flush();
Response.Close();
Response.End();
As the file I am creating can be written to disk, and can be read using Gzip, but the file received by the browser is corrupt, I am confident that my file creation is OK. But somehow after writing the file to the Response, it is being corrupted.
I did wonder if maybe some sort of HTTPHandler is manipulating the result, but I haven't added any Handlers (that I can see).
I am running the application locally currently through IISExpress. How can I check what HttpHandlers/HttpModules are being applied to the pipeline?
Ultimately I expect to receive the exact same file in my browser as is written to disk.
For reference, my generated content is 132 bytes in length, but the browser receives 216 bytes. I have noticed when looking at the byte structure of the received data, there is a repeating pattern of 3 bytes in the content, with the values 239, 191, 189. It almost looks like the resultant byte array has been stuffed or padded with these 3 bytes.
EDIT
Here is a standalone Action method which demonstrated the issue.
[HttpGet]
public void GetFile()
{
byte[] text = Encoding.ASCII.GetBytes(#"{""PetName"":""Doggy McDocFace"",""OwnerName"":""Kurt""}");
byte[] compressed = Compress(text);
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = "ExampleFile.vcp",
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = true,
};
System.IO.File.WriteAllBytes(#"C:\temp\ExampleFile.vcp", compressed);
Response.Clear();
Response.ContentType = "application/gzip";
Response.AppendHeader("Content-Disposition", cd.ToString());
Response.AddHeader("Content-Length", compressed.Length.ToString());
Response.BinaryWrite(compressed);
Response.Flush();
Response.Close();
Response.End();
}
public byte[] Compress(byte[] raw)
{
using (var memory = new MemoryStream())
{
using (var gzip = new GZipStream(memory, CompressionMode.Compress, true))
{
gzip.Write(raw, 0, raw.Length);
}
return memory.ToArray();
}
}
Here I am spoofing my JSON content, and then compressing it. The file written to disk works fine, and can be opened with my GZip application (I use 7-zip). However, the file received by the browser is corrupt. 7-zip cannot recognise it as a gzip file.
EDIT 2
So it looks like (Thanks to #Will) that the content when written to Response is falling foul of UTF-8 encoding. I cannot work out how though, as in my example above I am using Encoding.ASCII.GetBytes() to convert my string to a byte[] array.
I've tried setting the
Response.Charset = Encoding.ASCII.EncodingName;
Response.ContentEncoding = Encoding.ASCII;
But this still doesn't result in a valid file downloaded.
Edit 3
I've narrowed down the issue to the GZip encryption of the data. If I do not encrypt the data, then the plain text file downloads fine. However, encrypting the byte[] array and then writing that byte[] array to the Repsonse is resulting in what seems like UTF-8 encoding issues. Any bytes with a value over 127 are corrupted with the 3 bytes I mention further up. I cannot work out why the Response is treating this encrypted data in this way. My assumption is that when the Byte[] array is just plain text as a byte[] array, then this is handled fine. As soon as it is a proper byte[] array, i.e not just a string as a byte[] array, then some other conversion of encoding is going on in the Response.
You can try ActionFilterAttribute
Basically response filters look at the Response output stream as it's written and convert the data flowing through it.
GZip/Deflate Compression in ASP.NET MVC
public class CompressAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var encodingsAccepted = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
if (string.IsNullOrEmpty(encodingsAccepted)) return;
encodingsAccepted = encodingsAccepted.ToLowerInvariant();
var response = filterContext.HttpContext.Response;
if (encodingsAccepted.Contains("deflate"))
{
response.AppendHeader("Content-encoding", "deflate");
response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
}
else if (encodingsAccepted.Contains("gzip"))
{
response.AppendHeader("Content-encoding", "gzip");
response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
}
}
}
[Compress]
[HttpGet]
public ActionResult GetFile()
{...}
This is the C# I have downloaded for hiqpdf, but I'm not sure how to amend it so it will work with my html? There are errors coming up om my asp.net c# sheet for textBoxUrl but I'm not sure what namespace I should be using to grab this or whether I need to replace this text?
C# code:
using HiQPdf;
protected void Print_Button_Click(object sender, EventArgs e)
{
// create the HTML to PDF converter
HtmlToPdf htmlToPdfConverter = new HtmlToPdf();
// select the HTML element to be converted to PDF
htmlToPdfConverter.ConvertedHtmlElementSelector =
textBoxConvertedHtmlElementSelector.Text;
// convert URL to a PDF memory buffer
string url = textBoxUrl.Text;
byte[] pdfBuffer = htmlToPdfConverter.ConvertUrlToMemory(url);
// inform the browser about the binary data format
HttpContext.Current.Response.AddHeader("Content-Type",application/pdf");
// let the browser know how to open the PDF document
HttpContext.Current.Response.AddHeader("Content-Disposition",
String.Format("attachment; filename=ConvertHtmlPart.pdf;
size ={ 0}
",
pdfBuffer.Length.ToString()));
// write the PDF buffer to HTTP response
HttpContext.Current.Response.BinaryWrite(pdfBuffer);
// call End() method of HTTP response
// to stop ASP.NET page processing
HttpContext.Current.Response.End();
}
The textBoxUrl is a TextBox control. You should replace this with your source URL.
e.g for bbc site with "#page" selector.
using HiQPdf;
protected void Print_Button_Click(object sender, EventArgs e)
{
// create the HTML to PDF converter
HtmlToPdf htmlToPdfConverter = new HtmlToPdf();
// select the HTML element to be converted to PDF
htmlToPdfConverter.ConvertedHtmlElementSelector = "#page"
// convert URL to a PDF memory buffer
string url = "http://www.bbc.com/";
byte[] pdfBuffer = htmlToPdfConverter.ConvertUrlToMemory(url);
// inform the browser about the binary data format
HttpContext.Current.Response.AddHeader("Content-Type",application/pdf");
// let the browser know how to open the PDF document
HttpContext.Current.Response.AddHeader("Content-Disposition",
String.Format("attachment; filename=ConvertHtmlPart.pdf;
size ={ 0}
",
pdfBuffer.Length.ToString()));
// write the PDF buffer to HTTP response
HttpContext.Current.Response.BinaryWrite(pdfBuffer);
// call End() method of HTTP response
// to stop ASP.NET page processing
HttpContext.Current.Response.End();
}
I'm getting a file from a database in byte [] format and want user to see download dialog before Linq will take it from the database. It's in C# and ASP.NET.
Now, it's like this:
User choose a file, click on it.
In code I get id of file clicked and using Linq I'm downloading.
Then I send the file by Response.OutputStream.Write(content, 0,
content.Length);
Before a file is downloaded from the database user won't see any
download dialog.
What can I do if I want users to see the download dialog before file is downloaded?
Code:
Getting file by id:
public static byte[] getFile(Guid id)
{
var linqFile = from file in MyDB.Files
where file.IdPliku.Equals(id)
select new
{
Content = file.Content
};
return linqFile.ToList().FirstOrDefault().Content.ToArray();
}
Saving file:
public void SaveFile(Guid fileID, string filename, string mimeTypes)
{
try
{
byte[] content = FileService.getFile(fileID);
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = mimeTypes;
Response.AppendHeader("Accept-Ranges", "bytes");
Response.AppendHeader("Content-Range", string.Format("0-{0}/{1}", content.Length, content.Length));
Response.AppendHeader("Content-Length", content.Length.ToString());
Response.AppendHeader("Content-Encoding", "utf-8");
Response.AppendHeader("Content-Type", Response.ContentType);
Response.AppendHeader("Content-Disposition", "attachment; filename= " + HttpUtility.UrlEncode(filename));
Response.OutputStream.Write(content, 0, content.Length);
//Response.BinaryWrite(content);
Response.Flush();
}
finally
{
Response.Close();
}
}
You are my hope.
your issue is here:
byte[] content = FileService.getFile(fileID);
because in this line you allocate the whole file in the web server's RAM and put everything in there, all content of the file from the database; what happens later does not matter anymore because you have already downloaded from db to web server in this line!!!
I am having such Deja-vu because I am sure I have given exactly the same comment on a very same question few weeks ago. Can't find it now, search for something like this here in SO.
In fact the solution is to stream directly to the output stream of the Response avoiding your byte[] array allocation above, to get this your data layer should of course support it and if it does not you could add a method for this. You want to use SQL Server filestream or something similar.
If I have a URL to a download, www.website.com/myfile.html
so when that link is clicked it automatically starts a download, which may be myfile.txt for example, how would I get that file into C# for reading..
Is that what Net.WebRequest.Create(url), Net.HttpWebRequest does?
You could achieve this using WebClient:
using (var client = new WebClient())
{
// Download and save the file locally
client.DownloadFile("http://www.website.com/myfile.html", "myfile.html");
}
If you don't want to store the file locally but only read the contents you could try this:
using (var client = new WebClient())
{
string result = client.DownloadString("http://www.website.com/myfile.html");
}
Using C# as an example, here is how one might force the download of a file after clicking a button, link, etc...
public void DownloadFileLink_Click(object sender, EventArgs e)
{
//Get the file data
byte[] fileBytes = Artifacts.Provider.GetArtifact(ArtifactInfo.Id);
//Configure the response header and submit the file data to the response stream.
HttpContext.Current.Response.AddHeader("Content-disposition", "attachment;filename=" + "myDynamicFileName.txt");
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.BinaryWrite(fileBytes);
HttpContext.Current.Response.End();
}
With this in mind, what you need to look for is the Header in the response, the Header will contain an item Content-disposition which will contain the filename of the file being streamed in the response.