I'm downloading large file to a client through a browser. When I initiate the download content is written to body. I see the transfer in chrome to the user they can't see progress.
once it finishes the transfer and the method below finishes then you can see the file downloading. It seems like double work.
public async Task GetFile(string fileName, string fileToken)
{
Stream stream = null;
int bufferSize = 1048576;
byte[] buffer = new byte[bufferSize];
int length;
long lengthToRead;
try
{
string filePath = Path.Combine(_env.WebRootPath, $"Common{Path.DirectorySeparatorChar}", $"Files{Path.DirectorySeparatorChar}", fileToken);
stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
long dj = stream.Length / bufferSize;
if (stream.Length % bufferSize > 0)
{
dj += 1;
}
dj = dj * bufferSize;
lengthToRead = stream.Length;
Response.ContentType = "application/pdf";
Response.Headers.Add("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
Response.ContentLength = dj;
while( lengthToRead > 0)
{
length = stream.Read(buffer, 0, bufferSize);
await Response.BodyWriter.WriteAsync(buffer);
await Response.BodyWriter.FlushAsync();
lengthToRead = lengthToRead - length;
}
}
catch(Exception exp)
{
Response.ContentType = "text/html";
}
finally
{
if(stream != null)
{
stream.Close();
}
}
}
Related
I have a below scenario.
Client send request to Server-1 for file download
Server-1 send request to Server-2 for file.
To make this work I need to create a mechanism where once client send request to the Server-1, Server-1 will request to Server-2 which will send file as response output-stream in chunks. Server-1 will send this file chunks to client browser continuously as it keep receiving from server-2.
I have done code as below, theoretically it looks fine but still it is not working.
It is not downloading entire file in client browser, it seems like last chunk is not transferred to the Server-1 or it is not downloading to client browser from Server-1
Server-1 Code (Where client request for File download)
private void ProccesBufferedResponse(HttpWebRequest webRequest, HttpContext context)
{
char[] responseChars = null;
byte[] buffer = null;
if (webRequest == null)
logger.Error("Request string is null for Perfios Docs Download at ProccesBufferedResponse()");
context.Response.Buffer = false;
context.Response.BufferOutput = false;
try
{
WebResponse webResponse = webRequest.GetResponse();
context.Response.ContentType = "application/pdf";
context.Response.AddHeader("Content-disposition", webResponse.Headers["Content-disposition"]);
StreamReader responseStream = new StreamReader(webResponse.GetResponseStream());
while (!responseStream.EndOfStream)
{
responseChars = new char[responseStream.ToString().ToCharArray().Length];
responseStream.Read(responseChars, 0, responseChars.Length);
buffer = Encoding.ASCII.GetBytes(responseChars);
context.Response.Clear();
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.Flush();
}
}
catch (Exception ex)
{
throw;
}
finally
{
context.Response.Flush();
context.Response.End();
}
}
Server-2 Code (Where Server-1 will send request for file)
private void DownloadInstaPerfiosDoc(int CompanyID, string fileName, string Foldertype)
{
string folderPath;
string FilePath;
int chunkSize = 1024;
int startIndex = 0;
int endIndex = 0;
int length = 0;
byte[] bytes = null;
DirectoryInfo dir;
folderPath = GetDocumentDirectory(CompanyID, Foldertype);
FilePath = folderPath + "\\" + fileName;
dir = new DirectoryInfo(folderPath);
HttpContext.Current.Response.Buffer = false;
HttpContext.Current.Response.BufferOutput = false;
if (dir.Exists && dir.GetFiles().Length > 0)
{
foreach (var file in dir.GetFiles(fileName))
{
FilePath = folderPath + "\\" + file.Name;
FileStream fsReader = new FileStream(FilePath, FileMode.Open, FileAccess.Read);
HttpContext.Current.Response.ContentType = "application/pdf";
HttpContext.Current.Response.AddHeader("Content-disposition", string.Format("attachment; filename = \"{0}\"", fileName));
int totalChunks = (int)Math.Ceiling((double)fsReader.Length / chunkSize);
for (int i = 0; i < totalChunks; i++)
{
startIndex = i * chunkSize;
if (startIndex + chunkSize > fsReader.Length)
endIndex = (int)fsReader.Length;
else
endIndex = startIndex + chunkSize;
length = (int)endIndex - startIndex;
bytes = new byte[length];
fsReader.Read(bytes, 0, bytes.Length);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.OutputStream.Write(bytes, 0, bytes.Length);
HttpContext.Current.Response.Flush();
}
}
}
}
Please help me to resolve this issue.
It is possible and feasible. I'll give a pseudo procedure for you to understand the overall idea.
Server1
download action gets hit
create a request to server2
get the response stream of your server2 request
read the response stream in desired chunk sizes until it's consumed completely
write each chunk (as soon as you read) to current response stream
Server2
download action gets hit
write your stream onto your current response stream however you like
I have some PDFs stored in S3 and I'm trying to make a link that will download them. This is the method that I have so far:
public virtual ActionResult DownloadPDF(string filename)
{
string secretKey = this.UnitOfWork.ApplicationSettingRepository.GetOrCreateByName<string>("StorageProvider_AmazonS3_SecretKey");
string accessKey = this.UnitOfWork.ApplicationSettingRepository.GetOrCreateByName<string>("StorageProvider_AmazonS3_AccessKey");
var bucket = this.UnitOfWork.ApplicationSettingRepository.GetOrCreateByName<string>("StorageProvider_AmazonS3_BucketName");
var serviceUrl = this.UnitOfWork.ApplicationSettingRepository.GetOrCreateByName<string>("StorageProvider_AmazonS3_ServiceUrl");
AmazonS3Config config = new AmazonS3Config();
config.ServiceURL = serviceUrl;
var client = Amazon.AWSClientFactory.CreateAmazonS3Client(
accessKey,
secretKey,
config
);
GetObjectRequest request = new GetObjectRequest();
request.BucketName = bucket;
request.Key = "userfiles/MSD IMAGES/ProductDocumentation/" + filename;
GetObjectResponse response = client.GetObject(request);
response.WriteResponseStreamToFile("\\Downloads\\" + filename);
}
I got that method from this documentation here but I want to download the download to show in the browser, and go to the standard downloads folder. I created this method previously for downloading csv files:
var csv = new StringBuilder();
csv.AppendLine("col1,col2,col3");
var bytes = Encoding.UTF8.GetBytes(csv.ToString());
var response = new FileContentResult(bytes, "text/csv");
response.FileDownloadName = fileName;
return response;
I tried doing the same thing with the S3 file:
GetObjectResponse response = client.GetObject(request);
// response.WriteResponseStreamToFile("\\Downloads\\" + filename);
var bytes = Encoding.UTF8.GetBytes(response.ToString());
var download = new FileContentResult(bytes, "application/pdf");
download.FileDownloadName = filename;
return download;
it downloads a file, but the pdf doesn't work (it fails to load). How do I download the file that I got from S3?
Here's the working solution I came up with:
GetObjectResponse response = client.GetObject(request);
using (Stream responseStream = response.ResponseStream)
{
var bytes = ReadStream(responseStream);
var download = new FileContentResult(bytes, "application/pdf");
download.FileDownloadName = filename;
return download;
}
public static byte[] ReadStream(Stream responseStream)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
you would need to do something like the following
int byteCount
byte[] buffer = new byte[2048]; // read in chunks of 2KB
Stream responseStream= Response.GetResponseStream();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
while ((byteCount = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
Response.OutputStream.Write(buffer, 0, byteCount);
}
Response.Flush();
Response.Close();
Response.End();
My requirement is to transfer a zip file of size 400MB or more; The following code works for at least 40MB; But for more I would have to change byte[] bytes = new byte[50000000]; to byte[] bytes = new byte[400000000]; and maxRequestLength to maxRequestLength="409600";
The problem is byte[] bytes = new byte[100000000]; returns an error regarding insufficient space. So how can I transfer large files using WebClient??
WebClient client = new WebClient();
client.AllowWriteStreamBuffering = true;
UriBuilder ub = new UriBuilder("http://localhost:57596/UploadImages.ashx");
ub.Query = "ImageName=" + "DataSet" + DataSetId + ".zip";
client.OpenWriteCompleted += (InputStream, eArguments) =>
{
try
{
using (Stream output = eArguments.Result)
{
output.Write(ImagesAux, 0, (int)ImagesAux.Length);
//numeroimagem++;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
//throw;
}
};
client.OpenWriteAsync(ub.Uri);
in UploadImages.ashx
public void ProcessRequest(HttpContext context)
{
//context.Response.ContentType = "text/plain";
//context.Response.Write("Hello World");
string ImageName = context.Request.QueryString["ImageName"];
string UploadPath = context.Server.MapPath("~/ServerImages/");
using (FileStream stream = File.Create(UploadPath + ImageName))
{
byte[] bytes = new byte[50000000]; //
int bytesToRead = 0;
while ((bytesToRead =
context.Request.InputStream.Read(bytes, 0, bytes.Length)) != 0)
{
stream.Write(bytes, 0, bytesToRead);
stream.Close();
}
}
}
in Web.config
<httpRuntime targetFramework="4.5" maxRequestLength="40960"/>
You should never load everything in memory then write all back to disk, but instead you should load pieces and write them to disk while you are reading them.
When you've done reading you close the stream you are writing to.
Otherwise as soon as you reach sizes as GB you can get an OutOfMemory really quick.
So I would change the writing bytes to disk from this:
using (FileStream stream = File.Create(UploadPath + ImageName))
{
byte[] bytes = new byte[50000000]; //
int bytesToRead = 0;
while ((bytesToRead = context.Request.InputStream.Read(bytes, 0, bytes.Length)) != 0)
{
stream.Write(bytes, 0, bytesToRead);
stream.Close();
}
}
to this:
using (FileStream stream = File.Create(UploadPath + ImageName))
{
byte[] bytes = new byte[1024];
long totalBytes = context.Request.InputStream.Length;
long bytesRead = 0;
int bytesToRead = bytes.Length;
if (totalBytes - bytesRead < bytes.Length)
bytesToRead = (int)(totalBytes - bytesRead);
bytes = new byte[bytesToRead];
while ((bytesToRead = context.Request.InputStream.Read(bytes, bytesRead, bytes.Length)) != 0)
{
stream.Write(bytes, bytesRead, bytes.Length);
bytesRead += bytes.Length;
if (totalBytes - bytesRead < bytes.Length)
bytesToRead = (int)(totalBytes - bytesRead);
bytes = new byte[bytesToRead];
}
stream.Close();
}
1024 would be the buffer size.
i used the following code to download the file folder in asp.net website are
string path = #"E:\sample.zip";
FileInfo file = new FileInfo(path);
int len = (int)file.Length, bytes;
Response.ContentType = "text/html";
// Response.AddHeader "Content-Disposition", "attachment;filename=" + filename;
Response.AppendHeader("content-length", len.ToString());
byte[] buffer = new byte[1024];
using(Stream stream = File.OpenRead(path)) {
while (len > 0 && (bytes =
stream.Read(buffer, 0, buffer.Length)) > 0)
{
Response.OutputStream.Write(buffer, 0, bytes);
len -= bytes;
}
}
it works fine/...
but my problem is when i used the same code to the library file as
FileInfo file = new FileInfo(ZipPath);
int len = (int)file.Length, bytes;
HttpResponse Response = new HttpResponse(TextWriter.Null);
Response.ContentType = "text/html";
Response.AppendHeader("content-length", len.ToString());
byte[] buffer = new byte[1024];
using (Stream stream = File.OpenRead(ZipPath))
{
while (len > 0 && (bytes =
stream.Read(buffer, 0, buffer.Length)) > 0)
{
Response.OutputStream.Write(buffer, 0, bytes);
len -= bytes;
}
}
}
it throws me a error as
OutputStream is not available when a custom TextWriter is used.
i guess the problem is in that line
HttpResponse Response = new HttpResponse(TextWriter.Null);
can you provide me a solution
waiting for your responses....
You can try with this code
TextWriter sw = new StringWriter();
HttpResponse Response = new HttpResponse(sw);
i replaced the structures as
HttpResponse response = HttpContext.Current.Response;
it works fine.....
Thanks all for your support
Hi my download function.
protected void downloadFunction(string fileName)
{
string filePath = #"D:\SoftwareFiles\";
LogMessageToFile("Download started " + filePath + fileName);
byte[] array = File.ReadAllBytes(filePath + fileName);
Response.Clear();
Response.ContentType = "application/x-newton-compatible-pkg";
Response.AppendHeader("Content-Disposition",
"attachment;filename=" + fileName);
Response.BinaryWrite(array);
Response.End();
}
When handling filesize of 20, 200mb no problem.
When handling 1gb file, an exception is thrown:
Overflow or underflow in the arithmetic operation.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArithmeticException: Overflow or underflow in the arithmetic operation.
What to do?
My guess is that you're running out of memory in the byte[] array.
You can try breaking the file down and reading it in chunks.
I found a code example from a Google search to get you started:
C# file downloader AKA Response.BinaryWrite
using System;
using System.IO;
using System.Web;
public class Download
{
public static void SmallFile(string filename, string filepath, string contentType)
{
try
{
FileStream MyFileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read);
long FileSize;
FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
HttpContext.Current.Response.ContentType = contentType;
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8));
HttpContext.Current.Response.BinaryWrite(Buffer);
}
catch
{
HttpContext.Current.Response.ContentType = "text/html";
HttpContext.Current.Response.Write("Downloading Error!");
}
HttpContext.Current.Response.End();
}
public static void LargeFile(string filename, string filepath, string contentType)
{
Stream iStream = null;
// Buffer to read 10K bytes in chunk
//byte[] buffer = new Byte[10000];
// Buffer to read 1024K bytes in chunk
byte[] buffer = new Byte[1048576];
// Length of the file:
int length;
// Total bytes to read:
long dataToRead;
try
{
// Open the file.
iStream = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read);
// Total bytes to read:
dataToRead = iStream.Length;
HttpContext.Current.Response.ContentType = contentType;
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8));
// Read the bytes.
while (dataToRead > 0)
{
// Verify that the client is connected.
if (HttpContext.Current.Response.IsClientConnected)
{
// Read the data in buffer.
length = iStream.Read(buffer, 0, 10000);
// Write the data to the current output stream.
HttpContext.Current.Response.OutputStream.Write(buffer, 0, length);
// Flush the data to the HTML output.
HttpContext.Current.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.
//HttpContext.Current.Response.Write("Error : " + ex.Message);
HttpContext.Current.Response.ContentType = "text/html";
HttpContext.Current.Response.Write("Error : file not found");
}
finally
{
if (iStream != null)
{
//Close the file.
iStream.Close();
}
HttpContext.Current.Response.End();
HttpContext.Current.Response.Close();
}
}
public static void ResumableFile(string filename, string fullpath, string contentType)
{
try
{
FileStream myFile = new FileStream(fullpath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
BinaryReader br = new BinaryReader(myFile);
try
{
HttpContext.Current.Response.AddHeader("Accept-Ranges", "bytes");
HttpContext.Current.Response.Buffer = false;
long fileLength = myFile.Length;
long startBytes = 0;
//int pack = 10240; //10K bytes
int pack = 1048576; //1024K bytes
if (HttpContext.Current.Request.Headers["Range"] != null)
{
HttpContext.Current.Response.StatusCode = 206;
string[] range = HttpContext.Current.Request.Headers["Range"].Split(new char[] { '=', '-' });
startBytes = Convert.ToInt64(range[1]);
}
HttpContext.Current.Response.AddHeader("Content-Length", (fileLength - startBytes).ToString());
if (startBytes != 0)
{
HttpContext.Current.Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
}
HttpContext.Current.Response.AddHeader("Connection", "Keep-Alive");
HttpContext.Current.Response.ContentType = contentType;
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8));
br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
int maxCount = (int)Math.Floor((double)((fileLength - startBytes) / pack)) + 1;
for (int i = 0; i < maxCount; i++)
{
if (HttpContext.Current.Response.IsClientConnected)
{
HttpContext.Current.Response.BinaryWrite(br.ReadBytes(pack));
}
else
{
i = maxCount;
}
}
}
catch
{
HttpContext.Current.Response.ContentType = "text/html";
HttpContext.Current.Response.Write("Error : file not found");
}
finally
{
br.Close();
myFile.Close();
}
}
catch
{
HttpContext.Current.Response.ContentType = "text/html";
HttpContext.Current.Response.Write("Error : file not found");
}
}
}