I am new to C# and trying to write a download manager with HttpWebRequest.
my manager calls a new class for breaking file into chunks for every file and that class calls a new downloader class.
my code for download is:
private void StartDownload()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
Console.WriteLine("request");
request.AddRange((int)_startPos, (int)_endPos);
WebResponse res = request.GetResponse();
Console.WriteLine("res: "+res);
RequestContentLength = 0;
try
{
bool isFinished = false;
isDownloading = true;
double speed = 0;
using (var responseStream = res.GetResponseStream())
{
using (FileStream localFileStream = new FileStream(FilePath, FileMode.Append))
{
Console.WriteLine("download started - "+ Url);
var buffer = new byte[4096];
int bytesRead;
DateTime lastDate = DateTime.Now;
int lastDownloaded = 0;
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0 && isDownloading)
{
totalBytesRead += bytesRead;
if ((DateTime.Now - lastDate).TotalSeconds >= 1)
{
lastDate = DateTime.Now;
speed = totalBytesRead - lastDownloaded;
lastDownloaded = totalBytesRead;
}
RequestContentLength += bytesRead;
localFileStream.Write(buffer, 0, bytesRead);
double percentage = totalBytesRead * 100 / FileHeader.FileSize;
onDownloadProgress?.Invoke(ID, fname, percentage, totalBytesRead, FileHeader.FileSize, speed);
}
isFinished = true;
}
}
if (isFinished) {
onDownloadFinished?.Invoke(ID, fname, true, "success");
}
else
{
onDownloadFinished?.Invoke(ID, fname, false, "error");
}
isDownloading = false;
}
catch (Exception ex)
{
isDownloading = false;
onDownloadFinished?.Invoke(ID, fname, false, ex.Message);
}
}
the code works fine for the FIRST download ( I cropped the rest to make it shorter) but when I call it after first download I get Console.WriteLine("request"); to output but the rest stops and after a minute or two I get a timeout error at WebResponse res = request.GetResponse();
I couldn't name my exact problem and couldn't find a solution.
there are some similar posts about this and I tried most of them but doesn't work for me.
Related
So I am trying to download file with FTP and want to show current progress on a progress bar component. I run task like this with custom FtpClient class:
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog() == true)
{
pBar.Visibility = Visibility.Visible;
string status = "";
string filename = entry.FileName.Text;
await Task.Run(() =>
{
status = client.DownloadFile(filename, sfd.FileName, pBar);
});
statusBox.Text = status.Substring(4);
}
public string DownloadFile(string source, string dest, ProgressBar pBar)
{
FtpWebRequest sizeRequest = CreateRequest(CombinePaths(url, source), WebRequestMethods.Ftp.GetFileSize); // creates FtpWebRequest and assigns method
FtpWebResponse sizeResponse = (FtpWebResponse)sizeRequest.GetResponse();
if (sizeResponse.ContentLength <= 0) // if server does not support SIZE
pBar.IsIndeterminate = true;
else
{
pBar.IsIndeterminate = false; // fails here since progress bar is in the another thread
pBar.Maximum = sizeResponse.ContentLength;
pBar.Value = 0;
}
byte[] buffer = new byte[buffSize];
using (FtpWebResponse dlResponse = (FtpWebResponse)dlRequest.GetResponse())
{
using (Stream stream = dlResponse.GetResponseStream())
{
using (FileStream fs = new FileStream(dest, FileMode.OpenOrCreate))
{
int readCount = stream.Read(buffer, 0, buffSize);
while (readCount > 0)
{
fs.Write(buffer, 0, readCount);
pBar.Value = pBar.Value + readCount;
readCount = stream.Read(buffer, 0, buffSize);
}
}
}
return dlResponse.StatusDescription;
}
Is there a way to make this work? Since I am downloading and trying to update UI I don't know is there is a way to do what I want
pBar.Dispatcher.Invoke(() =>
{
if (GetFileSize(source) <= 0)
pBar.IsIndeterminate = true;
else
{
pBar.IsIndeterminate = false;
pBar.Maximum = GetFileSize(source);
pBar.Value = 0;
}
});
// code...
pBar.Dispatcher.Invoke(() =>
{
pBar.Value = pBar.Value + readCount;
});
Used dispatcher to update progress bar, works flawlessly
I am working on streaming mp3 in real time using NAudio library. I have prepared a code to do it and it works good in general except while another media is playing in another process (e.g. Youtube, local mp3 players..), . At that time, mp3 is stuttering. I don't know why this is happening. How to solve this problem?
Code to stream in Console Application. Copy paste and you can try it.
static void Main(string[] args)
{
AcmMp3FrameDecompressor decompressor = null;
BufferedWaveProvider provider = null;
WaveFormat mp3format = null;
WaveOut waveOut = new WaveOut();
long size = 0;
byte[] decbuffer = new byte[50 * 1024];
//I am using mp3 links converted by listentoyoutube.com
//But links will be expired, so I didnt put any
string url = "";
string path = Path.Combine(Path.GetTempPath(), "test.mp3");
CheckUrlandCreateTools(url, ref decompressor, ref mp3format, ref size);
FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
Stream remote = resp.GetResponseStream();
Mp3Frame frame = null;
MemoryStream ms = null;
byte[] buffer = new byte[1024];
int read = 0;
long offset = 0;
provider = new BufferedWaveProvider(decompressor.OutputFormat);
provider.BufferDuration = TimeSpan.FromSeconds(20);
waveOut.Init(provider);
waveOut.Play();
while (waveOut.PlaybackState == PlaybackState.Playing)
{
if((read = remote.Read(buffer, 0, buffer.Length)) > 0)
fs.Write(buffer, 0, read);
fs.Flush();
ms = new MemoryStream(ReadStreamPartially(fs, offset, 100 * 1024));
try
{
frame = Mp3Frame.LoadFromStream(ms);
if (frame == null)
continue;
}
catch
{
continue;
}
offset += ms.Position;
int decompressed = decompressor.DecompressFrame(frame, decbuffer, 0);
provider.AddSamples(decbuffer, 0, decompressed);
if (IsBufferNearlyFull(provider))
Thread.Sleep(500);
}
}
public static byte[] ReadStreamPartially(System.IO.FileStream stream,
long offset, long count)
{
long originalPosition = stream.Position;
stream.Position = offset;
byte[] readBuffer = new byte[4096];
byte[] total = new byte[count];
int totalBytesRead = 0;
int byteRead;
while ((byteRead = stream.ReadByte()) != -1)
{
Buffer.SetByte(total, totalBytesRead, (byte)byteRead);
totalBytesRead++;
if (totalBytesRead == count)
break;
}
if (totalBytesRead < count)
{
byte[] temp = new byte[totalBytesRead];
Buffer.BlockCopy(total, 0, temp, 0, totalBytesRead);
stream.Position = originalPosition;
return temp;
}
stream.Position = originalPosition;
return total;
}
public static bool IsBufferNearlyFull(BufferedWaveProvider bufferedWaveProvider)
{
return bufferedWaveProvider != null &&
bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes
< bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4;
}
public static void CheckUrlandCreateTools(string url, ref AcmMp3FrameDecompressor decompressor,
ref WaveFormat format, ref long size)
{
HttpWebRequest req = SendRequest(url, 0, 0);
HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
size = resp.ContentLength;
Stream str = resp.GetResponseStream();
byte[] buffer = new byte[1024];
byte[] storer = new byte[1024 * 100];
int bytesRead = 0;
int total = 0;
while ((bytesRead = str.Read(buffer, 0, buffer.Length)) > 0)
{
Buffer.BlockCopy(buffer, 0, storer, total, bytesRead);
total += bytesRead;
Mp3Frame frame = Mp3Frame.LoadFromStream(new MemoryStream(storer));
if (frame == null) continue;
format = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2,
frame.FrameLength, frame.BitRate);
decompressor = new AcmMp3FrameDecompressor(format);
req.Abort();
resp.Close();
str.Close();
break;
}
}
public static HttpWebRequest SendRequest(string url, long from, long to)
{
HttpWebRequest req = WebRequest.Create(url) as HttpWebRequest;
req.Credentials = CredentialCache.DefaultCredentials;
req.Accept = "*/*";
req.KeepAlive = false;
req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
req.AllowAutoRedirect = true;
if (to > 0)
req.AddRange(from, to);
return req;
}
}
I am trying to ping a link using the code below and the it fails. It works fine in our development environment, but not in production. However, this a valid URL because I can copy and paste it in the browser and go to the site. Since this is in production, I cannot debug it locally on my machine. Can anyone think of what might be causing this? The issue is only with this link; other links work fine. The link I am trying is https://owl.english.purdue.edu/
System.Net.WebResponse objWebResponse = default(System.Net.WebResponse);
System.Net.WebRequest objWebRequest = System.Net.WebRequest.Create(URL);
objWebRequest.Timeout = TimeoutInMiliseconds;
try
{
PerformanceTimer.Start();
objWebResponse = objWebRequest.GetResponse();
PerformanceTimer.Finish();
try
{
this._ResponseTime = PerformanceTimer.ElapsedTime.Miliseconds;
}
catch (Exception ex)
{
string s = ex.ToString();
this._ResponseTime = -1;
}
{
this._ContentLength = objWebResponse.ContentLength;
this._ContentType = objWebResponse.ContentType;
int BytesRead = 0;
byte[] buffer = new byte[10001];
if (GetFileContent)
{
System.IO.Stream ReceiveStream = objWebResponse.GetResponseStream();
System.IO.MemoryStream MS = new System.IO.MemoryStream();
//memory stream is used so we can grab binary data
do
{
BytesRead = ReceiveStream.Read(buffer, 0, buffer.Length);
if (BytesRead <= 0) break;
MS.Write(buffer, 0, BytesRead);
}
while (true);
this._MemoryStream = MS;
ReceiveStream = null;
}
else
{
//This else is to validate the existence of the file, but not get their contents in case the file is big.
BytesRead = objWebResponse.GetResponseStream().Read(buffer, 0, buffer.Length);
if (BytesRead > 0)
{
this._MemoryStream = new System.IO.MemoryStream();
this._MemoryStream.Write(buffer, 0, BytesRead);
}
}
objWebResponse.Close();
}
functionReturnValue = true;
}
catch (System.Net.WebException ex)
{
this._Message = ex.Message;
}
catch (Exception ex2)
{
this._Message = ex2.Message;
}
objWebResponse = null;
objWebRequest = null;
PerformanceTimer = null;
return functionReturnValue;
When cancelling WriteAsync operation on a stream created by HttpWebRequest.GetRequestStream() I get a WebException with the following message:
Cannot close stream until all bytes are written
How can I clean up my HttpWebRequest and my stream when I cancel the operation using CancellationToken?
Here is a PART of my working code(I cleaned some headers for this post so the request may be malformed):
public async Task<bool> UploadAsync(FileInfo fi, CancellationToken ct, IProgress<int> progress = null)
{
FileStream fs = new FileStream(fi.FullName, FileMode.Open);
int bufferSize = 1 * 1024 * 1024;
byte[] buffer = new byte[bufferSize];
int len;
long position = fs.Position;
long totalWrittenBytes = 0;
HttpWebRequest uploadRequest = null;
Stream putStream = null;
try
{
while ((len = fs.Read(buffer, 0, buffer.Length)) > 0)
{
uploadRequest = (HttpWebRequest)WebRequest.Create("http://myuploadUrl");
uploadRequest.Method = "PUT";
uploadRequest.AddRange(position, position + len - 1);
uploadRequest.ContentLength = len;
putStream = uploadRequest.GetRequestStream();
int posi = 0;
int bytesLeft = len;
int chunkSize;
while (bytesLeft > 0)
{
chunkSize = Math.Min(25 * 1024, bytesLeft); //25KB
//HERE IS the WriteAsync part that is being cancelled
await putStream.WriteAsync(buffer, posi, chunkSize, ct);
bytesLeft -= chunkSize;
posi += chunkSize;
totalWrittenBytes += chunkSize;
if (progress != null)
progress.Report((int)(totalWrittenBytes * 100 / fs.Length));
}
putStream.Close();
putStream = null;
position = fs.Position;
var putResponse = (HttpWebResponse)uploadRequest.GetResponse();
putResponse.Close();
uploadRequest = null;
}
//putStream.Write(buffer, 0, len);
}
catch (OperationCanceledException ex)
{
if (putStream != null)
{
putStream.Flush();
putStream.Close();//WebException occur here: Cannot close stream until all bytes are written.
}
return false;
}
finally{
fs.Close();
}
return true;
}
I finally catched the WebException because a post request must write the expected number of bytes (ContentLength property)
Here is my final catch
catch (OperationCanceledException ex)
{
try
{
if (putStream != null)
putStream.Dispose();
}
catch (WebException) { }
return false;
}
finally{
fs.Close();
}
Perhaps I am not sure if I should bother trying to dispose the stream?
The idea is that if I selected more than one file then upload one file when, then when it finishes to upload then upload the next one and so on.
I changed the filedialoug to be able to select multi:
private void btnBrowse_Click(object sender, EventArgs e)
{
openFileDialog1.Multiselect = true;
if (this.openFileDialog1.ShowDialog() != DialogResult.Cancel)
this.txtUploadFile.Text = this.openFileDialog1.FileName;
}
I can select more than one file the problem is that when using a breakpoint I see on this.txtUploadFile.Text one file only the first I selected. txtUploadFile is a textBox.
The first thing is: how can I see on the textBox(txtUploadFile) all the selected files and not only one? Or how can I get some indication that all the files I selected really selected? Maybe to show somehow in the textBox something that all files selected?
The important part is: how do I upload all the selected files one by one?
This is the button click event that starting the files uploading:
private void btnUpload_Click(object sender, EventArgs e)
{
if(this.ftpProgress1.IsBusy)
{
this.ftpProgress1.CancelAsync();
this.btnUpload.Text = "Upload";
}
else
{
FtpSettings f = new FtpSettings();
f.Host = this.txtHost.Text;
f.Username = this.txtUsername.Text;
f.Password = this.txtPassword.Text;
f.TargetFolder = this.txtDir.Text;
f.SourceFile = this.txtUploadFile.Text;
f.Passive = this.chkPassive.Checked;
try
{
f.Port = Int32.Parse(this.txtPort.Text);
}
catch { }
this.toolStripProgressBar1.Visible = true;
this.ftpProgress1.RunWorkerAsync(f);
this.btnUpload.Text = "Cancel";
}
}
I also have a backgroundworker with a progressBar so each file that is uploading the progressBar should show the progress of the uploading today it's showing for one file since I can upload only one file:
private void ftpProgress1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.toolStripStatusLabel1.Text = e.UserState.ToString(); // The message will be something like: 45 Kb / 102.12 Mb
this.toolStripProgressBar1.Value = Math.Min(this.toolStripProgressBar1.Maximum, e.ProgressPercentage);
}
private void ftpProgress1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
MessageBox.Show(e.Error.ToString(), "FTP error");
else if(e.Cancelled)
this.toolStripStatusLabel1.Text = "Upload Cancelled";
else
this.toolStripStatusLabel1.Text = "Upload Complete";
this.btnUpload.Text = "Upload";
this.toolStripProgressBar1.Visible = true;
}
And last the class of the FTP uploader:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
namespace FTP_ProgressBar
{
public partial class FtpProgress : BackgroundWorker
{
public static string ConnectionError;
private FtpSettings f;
public FtpProgress()
{
InitializeComponent();
}
public FtpProgress(IContainer container)
{
container.Add(this);
InitializeComponent();
}
private void FtpProgress_DoWork(object sender, DoWorkEventArgs e)
{
try
{
BackgroundWorker bw = sender as BackgroundWorker;
f = e.Argument as FtpSettings;
string UploadPath = String.Format("{0}/{1}{2}", f.Host, f.TargetFolder == "" ? "" : f.TargetFolder + "/", Path.GetFileName(f.SourceFile));
if (!UploadPath.ToLower().StartsWith("ftp://"))
UploadPath = "ftp://" + UploadPath;
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(UploadPath);
request.UseBinary = true;
request.UsePassive = f.Passive;
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Timeout = 300000;
request.Credentials = new NetworkCredential(f.Username, f.Password);
long FileSize = new FileInfo(f.SourceFile).Length;
string FileSizeDescription = GetFileSize(FileSize);
int ChunkSize = 4096, NumRetries = 0, MaxRetries = 50;
long SentBytes = 0;
byte[] Buffer = new byte[ChunkSize];
using (Stream requestStream = request.GetRequestStream())
{
using (FileStream fs = File.Open(f.SourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
int BytesRead = fs.Read(Buffer, 0, ChunkSize);
while (BytesRead > 0)
{
try
{
if (bw.CancellationPending)
return;
requestStream.Write(Buffer, 0, BytesRead);
SentBytes += BytesRead;
string SummaryText = String.Format("Transferred {0} / {1}", GetFileSize(SentBytes), FileSizeDescription);
bw.ReportProgress((int)(((decimal)SentBytes / (decimal)FileSize) * 100), SummaryText);
}
catch (Exception ex)
{
Debug.WriteLine("Exception: " + ex.ToString());
if (NumRetries++ < MaxRetries)
{
fs.Position -= BytesRead;
}
else
{
throw new Exception(String.Format("Error occurred during upload, too many retries. \n{0}", ex.ToString()));
}
}
BytesRead = fs.Read(Buffer, 0, ChunkSize);
}
}
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
System.Diagnostics.Debug.WriteLine(String.Format("Upload File Complete, status {0}", response.StatusDescription));
}
catch (WebException ex)
{
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
ConnectionError = "Error: Please check the ftp address";
break;
case WebExceptionStatus.Timeout:
ConnectionError = "Error: Timout Request";
break;
}
}
}
public static string GetFileSize(long numBytes)
{
string fileSize = "";
if(numBytes > 1073741824)
fileSize = String.Format("{0:0.00} Gb", (double)numBytes / 1073741824);
else if(numBytes > 1048576)
fileSize = String.Format("{0:0.00} Mb", (double)numBytes / 1048576);
else
fileSize = String.Format("{0:0} Kb", (double)numBytes / 1024);
if(fileSize == "0 Kb")
fileSize = "1 Kb";
return fileSize;
}
}
public class FtpSettings
{
public string Host, Username, Password, TargetFolder, SourceFile;
public bool Passive;
public int Port = 21;
}
}
It is a bit long, but I couldn't find a way to narrow the code since it's all connected to each other.
What I need it to do is if I selected more than one file make something like a queue and automatic upload one file when finished upload the next file and so on until the last file.
Today I can upload only one file. Single file each time.
I need that it will automatically upload all the selected files. Using the progressBar and the backgroundworker like it is now.
Here is a procedure how to upload multiple files from the directory where ftp - an address of ftp server:
private void uploadFTP(DirectoryInfo d, string ftp)
{
FileInfo[] flist = d.GetFiles();
if (flist.GetLength(0) > 0)
{
foreach (FileInfo txf in flist)
{
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(ftp + txf.Name);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential("username", "password");
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
FileStream stream = File.OpenRead(txf.FullName);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
stream.Close();
Stream reqStream = request.GetRequestStream();
reqStream.Write(buffer, 0, buffer.Length);
reqStream.Close();
txf.Delete();
}
}
}
You should be using
OpenFileDialog1.FileNames
And iterate on those elements to upload them one by one.
FileNames Property