make download resumable using c#
Resuming files is done by specifying the byte range of the file you would like to download using the Range HTTP header. This can be done in .NET with the HttpWebRequest.AddRange function.
For example:
request.AddRange(1000);
how resume able file download in asp.net with c# -> best way (for large files too)
downloading with pause and resume
File Upload and Download in Angular 9 With Web API and SQL
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if(ISSUE_DOWNLOAD_STATUS.intValue()==ECMConstant.ECM_DOWNLOADING){
File file=new File(DESTINATION_PATH);
if(file.exists()){
downloaded = (int) file.length();
connection.setRequestProperty("Range", "bytes="+(file.length())+"-");
}
}else{
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
}
connection.setDoInput(true);
connection.setDoOutput(true);
progressBar.setMax(connection.getContentLength());
in = new BufferedInputStream(connection.getInputStream());
fos=(downloaded==0)? new FileOutputStream(DESTINATION_PATH): new FileOutputStream(DESTINATION_PATH,true);
bout = new BufferedOutputStream(fos, 1024);
byte[] data = new byte[1024];
int x = 0;
while ((x = in.read(data, 0, 1024)) >= 0) {
bout.write(data, 0, x);
downloaded += x;
progressBar.setProgress(downloaded);
}
/*
* @param callback = To update the UI with appropriate action
* @param fileName = Name of the file by which downloaded file will be saved.
* @param downloadURL = File downloading URL
* @param filePath = Path where file will be saved
* @param object = Any object you want in return after download is completed to do certain operations like insert in DB or show toast
*/
public void startDownload(final IDownloadCallback callback, String fileName, String downloadURL, String filePath, Object object) {
callback.onPreExecute(); // Callback to tell that the downloading is going to start
int count = 0;
File outputFile = null; // Path where file will be downloaded
try {
File file = new File(filePath);
file.mkdirs();
long range = 0;
outputFile = new File(file, fileName);
/**
* Check whether the file exists or not
* If file doesn't exists then create the new file and range will be zero.
* But if file exists then get the length of file which will be the starting range,
* from where the file will be downloaded
*/
if (!outputFile.exists()) {
outputFile.createNewFile();
range = 0;
} else {
range = outputFile.length();
}
//Open the Connection
URL url = new URL(downloadURL);
URLConnection con = url.openConnection();
// Set the range parameter in header and give the range from where you want to start the downloading
con.setRequestProperty("Range", "bytes=" + range + "-");
/**
* The total length of file will be the total content length given by the server + range.
* Example: Suppose you have a file whose size is 1MB and you had already downloaded 500KB of it.
* Then you will pass in Header as "Range":"bytes=500000".
* Now the con.getContentLength() will be 500KB and range will be 500KB.
* So by adding the two you will get the total length of file which will be 1 MB
*/
final long lenghtOfFile = (int) con.getContentLength() + range;
FileOutputStream fileOutputStream = new FileOutputStream(outputFile, true);
InputStream inputStream = con.getInputStream();
byte[] buffer = new byte[1024];
long total = range;
/**
* Download the save the content into file
*/
while ((count = inputStream.read(buffer)) != -1) {
total += count;
int progress = (int) (total * 100 / lenghtOfFile);
EntityDownloadProgress entityDownloadProgress = new EntityDownloadProgress();
entityDownloadProgress.setProgress(progress);
entityDownloadProgress.setDownloadedSize(total);
entityDownloadProgress.setFileSize(lenghtOfFile);
callback.showProgress(entityDownloadProgress);
fileOutputStream.write(buffer, 0, count);
}
//Close the outputstream
fileOutputStream.close();
// Disconnect the Connection
if (con instanceof HttpsURLConnection) {
((HttpsURLConnection) con).disconnect();
} else if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).disconnect();
}
inputStream.close();
/**
* If file size is equal then return callback as success with downlaoded filepath and the object
* else return failure
*/
if (lenghtOfFile == outputFile.length()) {
callback.onSuccess(outputFile.getAbsolutePath(), object);
} else {
callback.onFailure(object);
}
} catch (Exception e) {
e.printStackTrace();
callback.onFailure(object);
}
}
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if(ISSUE_DOWNLOAD_STATUS.intValue()==ECMConstant.ECM_DOWNLOADING){
File file=new File(DESTINATION_PATH);
if(file.exists()){
downloaded = (int) file.length();
connection.setRequestProperty("Range", "bytes="+(file.length())+"-");
}
}else{
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
}
connection.setDoInput(true);
connection.setDoOutput(true);
progressBar.setMax(connection.getContentLength());
in = new BufferedInputStream(connection.getInputStream());
fos=(downloaded==0)? new FileOutputStream(DESTINATION_PATH): new FileOutputStream(DESTINATION_PATH,true);
bout = new BufferedOutputStream(fos, 1024);
byte[] data = new byte[1024];
int x = 0;
while ((x = in.read(data, 0, 1024)) >= 0) {
bout.write(data, 0, x);
downloaded += x;
progressBar.setProgress(downloaded);
}
By default all HTTP/1.1 compliant servers accept range headers and respond with content-range headers. (sysadmin part)
You don't need to change anything on webserver for ul/dl. Everything depends on how clients are downloading and uploading files. Maybe it's not the mechanism what you are looking for. (development part)
I think this is question for https://stackoverflow.com/
static void DownloadFile(string sSourceURL, string sDestinationPath)
{
long iFileSize = 0;
int iBufferSize = 1024;
iBufferSize *= 1000;
long iExistLen = 0;
System.IO.FileStream saveFileStream;
if (System.IO.File.Exists(sDestinationPath))
{
System.IO.FileInfo fINfo =
new System.IO.FileInfo(sDestinationPath);
iExistLen = fINfo.Length;
}
if (iExistLen > 0)
saveFileStream = new System.IO.FileStream(sDestinationPath,
System.IO.FileMode.Append, System.IO.FileAccess.Write,
System.IO.FileShare.ReadWrite);
else
saveFileStream = new System.IO.FileStream(sDestinationPath,
System.IO.FileMode.Create, System.IO.FileAccess.Write,
System.IO.FileShare.ReadWrite);
System.Net.HttpWebRequest hwRq;
System.Net.HttpWebResponse hwRes;
hwRq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(sSourceURL);
hwRq.AddRange((int)iExistLen);
System.IO.Stream smRespStream;
hwRes = (System.Net.HttpWebResponse)hwRq.GetResponse();
smRespStream = hwRes.GetResponseStream();
iFileSize = hwRes.ContentLength;
int iByteSize;
byte[] downBuffer = new byte[iBufferSize];
while ((iByteSize = smRespStream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
saveFileStream.Write(downBuffer, 0, iByteSize);
}
}
public static bool DownloadFileMethod(HttpContext httpContext, string filePath, long speed)
{
// Many changes: mostly declare variables near use
// Extracted duplicate references to HttpContext.Response and .Request
// also duplicate reference to .HttpMethod
// Removed try/catch blocks which hid any problems
var response = httpContext.Response;
var request = httpContext.Request;
var method = request.HttpMethod.ToUpper();
if (method != "GET" &&
method != "HEAD")
{
response.StatusCode = 501;
return false;
}
if (!File.Exists(filePath))
{
response.StatusCode = 404;
return false;
}
// Stream implements IDisposable so should be in a using block
using (var myFile = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var fileLength = myFile.Length;
if (fileLength > Int32.MaxValue)
{
response.StatusCode = 413;
return false;
}
var lastUpdateTiemStr = File.GetLastWriteTimeUtc(filePath).ToString("r");
var fileName = Path.GetFileName(filePath);
var fileNameUrlEncoded = HttpUtility.UrlEncode(fileName, Encoding.UTF8);
var eTag = fileNameUrlEncoded + lastUpdateTiemStr;
var ifRange = request.Headers["If-Range"];
if (ifRange != null && ifRange.Replace("\"", "") != eTag)
{
response.StatusCode = 412;
return false;
}
long startBytes = 0;
// Just guessing, but I bet you want startBytes calculated before
// using to calculate content-length
var rangeHeader = request.Headers["Range"];
if (rangeHeader != null)
{
response.StatusCode = 206;
var range = rangeHeader.Split(new[] {'=', '-'});
startBytes = Convert.ToInt64(range[1]);
if (startBytes < 0 || startBytes >= fileLength)
{
// TODO: Find correct status code
response.StatusCode = (int) HttpStatusCode.BadRequest;
response.StatusDescription =
string.Format("Invalid start of range: {0}", startBytes);
return false;
}
}
response.Clear();
response.Buffer = false;
response.AddHeader("Content-MD5", GetMD5Hash(filePath));
response.AddHeader("Accept-Ranges", "bytes");
response.AppendHeader("ETag", string.Format("\"{0}\"", eTag));
response.AppendHeader("Last-Modified", lastUpdateTiemStr);
response.ContentType = "application/octet-stream";
response.AddHeader("Content-Disposition", "attachment;filename=" +
fileNameUrlEncoded.Replace("+", "%20"));
var remaining = fileLength - startBytes;
response.AddHeader("Content-Length", remaining.ToString());
response.AddHeader("Connection", "Keep-Alive");
response.ContentEncoding = Encoding.UTF8;
if (startBytes > 0)
{
response.AddHeader("Content-Range",
string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
}
// BinaryReader implements IDisposable so should be in a using block
using (var br = new BinaryReader(myFile))
{
br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
const int packSize = 1024*10; //read in block,every block 10K bytes
var maxCount = (int) Math.Ceiling((remaining + 0.0)/packSize); //download in block
for (var i = 0; i < maxCount && response.IsClientConnected; i++)
{
response.BinaryWrite(br.ReadBytes(packSize));
response.Flush();
// HACK: Unexplained sleep
var sleep = (int) Math.Ceiling(1000.0*packSize/speed); //the number of millisecond
if (sleep > 1) Thread.Sleep(sleep);
}
}
}
return true;
}
HERE IS THE LIST OF BEST ANDROID NETWORKING LIBRARIES OR DEPENDENCIES
-
OKHTTP (HTTP + HTTP /2)
-
RETROFIT
-
ION (KOUSH)
-
ANDROIDASYNC (KOUSH)
-
FAST ANDROID NETWORKING LIBRARY
-
Volley
-
Android Download Manager Tutorial: How to Download Files using Download Manager from Internet
ASP.NET Web API and HTTP Byte Range Support
public class RangeController : ApiController
{
// Sample content used to demonstrate range requests
private static readonly byte[] _content = Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyz");
// Content type for our body
private static readonly MediaTypeHeaderValue _mediaType = MediaTypeHeaderValue.Parse("text/plain");
public HttpResponseMessage Get()
{
// A MemoryStream is seekable allowing us to do ranges over it. Same goes for FileStream.
MemoryStream memStream = new MemoryStream(_content);
// Check to see if this is a range request (i.e. contains a Range header field)
// Range requests can also be made conditional using the If-Range header field. This can be
// used to generate a request which says: send me the range if the content hasn't changed;
// otherwise send me the whole thing.
if (Request.Headers.Range != null)
{
try
{
HttpResponseMessage partialResponse = Request.CreateResponse(HttpStatusCode.PartialContent);
partialResponse.Content = new ByteRangeStreamContent(memStream, Request.Headers.Range, _mediaType);
return partialResponse;
}
catch (InvalidByteRangeException invalidByteRangeException)
{
return Request.CreateErrorResponse(invalidByteRangeException);
throw;
}
}
else
{
// If it is not a range request we just send the whole thing as normal
HttpResponseMessage fullResponse = Request.CreateResponse(HttpStatusCode.OK);
fullResponse.Content = new StreamContent(memStream);
fullResponse.Content.Headers.ContentType = _mediaType;
return fullResponse;
}
}
}
-
<dependentAssembly> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.0.0.0" /> </dependentAssembly>
// Including my setup of the range values this time: var range = Request.Headers.Range; long chunkLength = 2500000; long? beginRange = range.Ranges.First().From; long? endRange = range.Ranges.First().To; if (endRange == null) { if ((beginRange + chunkLength) > myBlob.Properties.Length) { endRange = myBlob.Properties.Length - 1; } else { endRange = beginRange + chunkLength; } } var blobStreamPosition = beginRange.Value; // Set the stream position blobStream.Position = blobStreamPosition; int bytesToRead = (int)(endRange - blobStreamPosition + 1); // Using BinaryReader for convenience BinaryReader binaryReader = new BinaryReader(blobStream); byte[] blobByteArray = binaryReader.ReadBytes(bytesToRead); message.Content = new ByteArrayContent(blobByteArray); // Don't forget that now you have to set the content range header yourself: message.Content.Headers.ContentRange = new ContentRangeHeaderValue(blobStreamPosition, endRange.Value, myBlob.Properties.Length); message.Content.Headers.ContentType = new MediaTypeHeaderValue(myBlob.Properties.ContentType); binaryReader.Dispose(); blobStream.Dispose();
-
Returning binary file from controller in ASP.NET Web API
using System; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Http; class FileResult : IHttpActionResult { private readonly string _filePath; private readonly string _contentType; public FileResult(string filePath, string contentType = null) { if (filePath == null) throw new ArgumentNullException("filePath"); _filePath = filePath; _contentType = contentType; } public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(File.OpenRead(_filePath)) }; var contentType = _contentType ?? MimeMapping.GetMimeMapping(Path.GetExtension(_filePath)); response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType); return Task.FromResult(response); } }
[Route("Images/{*imagePath}")] public IHttpActionResult GetImage(string imagePath) { var serverPath = Path.Combine(_rootPath, imagePath); var fileInfo = new FileInfo(serverPath); return !fileInfo.Exists ? (IHttpActionResult) NotFound() : new FileResult(fileInfo.FullName); }
-
How to work with HTTP Range Headers in WebAPI
Range requests enable you to retrieve partial content in lieu of the entire content for improved performance when working with WebAPI over HTTP.
HTTP 1.1 provides support for range headers – it allows you to specify range headers to enable the client to retrieve partial response. This can be used to retrieve partial content when working over HTTP. It's a technique that's widely used when streaming video files, for instance.
With this approach, rather than retrieving the entire data at one go, your application can retrieve the data partially, in chunks. This feature can be particularly helpful in scenarios when the client needs to recover the data from interrupted connections that might occur due to cancelled requests, or connections to the server that have been dropped.
The HTTP 206 Partial Content status code and its associated headers enable the clients or the web browsers to retrieve partial content in lieu of the complete content when a request is sent. Note that most modern-day web servers provide support for range requests. The ByteRangeStreamContent class that has been added to WebAPI makes it easier to work with range requests. The ByteRangeStreamContent class works similarly to how, let's say, StreamContent works – with the exception that it can provide a view over a stream that is seekable. Note that a MemoryStream or a FileStream is seekable, i.e., they can be used to do ranges over them.
Let’s now implement a simple WebAPI with support for HTTP Range Header using the ByteRangeStreamContent class. Here's a quick glance at how this all would work.
- The client connects to the server and requests for partial content by specifying the values in the Range header.
- The server responds by sending partial content if range is available with the status code as HTTP 206.
- If the requested range is not available, the server responds with HTTP 416.
And, now the implementation.
The following code snippet illustrates a WebAPI controller method with support for range headers. For the sake of simplicity I have hard-coded a string as data that would be retrieved as partial response by the client. You can change this implementation to work with data of much larger sizes, like files, large arrays, lists, etc.
public class Controller : ApiController { public HttpResponseMessage GetData() { try { MemoryStream memoryStream = new MemoryStream(data); if (Request.Headers.Range != null) { HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.PartialContent); response.Content = new ByteRangeStreamContent(memoryStream, Request.Headers.Range, MediaTypeHeaderValue.Parse("application/json")); return response; } else { HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StreamContent(memoryStream); response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); return response; } } catch (Exception ex) { return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex); } } } public class ApiClient { public void Run() { using (var httpclient = new HttpClient()) { httpclient.DefaultRequestHeaders.Range = new RangeHeaderValue(0, 5); var response = await httpclient.GetAsync(url, HttpCompletionOption.ResponseContentRead); using (var stream = await response.Content.ReadAsStreamAsync()) { var data = new byte[5]; int count = stream.Read(data, 0, data.Length); MemoryStream memoryStream = new MemoryStream(data); StreamReader reader = new StreamReader(memoryStream); string text = reader.ReadToEnd(); } } } }
Progressive Download Support in ASP.NET Web API