Download file from FTP with Progress - TotalBytesToReceive is always -1? - c#

I am trying to download a file from an FTP server with a progress bar.
The file is downloading, and the ProgressChanged event is calling, except in the event args TotalBytesToReceive is always -1. TotalBytes increases, but I am unable to calculate the percentage without the total.
I imagine I could find the file size through other ftp commands, but I wonder why this doesn't work?
My code:
FTPClient request = new FTPClient();
request.Credentials = credentials;
request.DownloadProgressChanged += new DownloadProgressChangedEventHandler(request_DownloadProgressChanged);
//request.DownloadDataCompleted += new DownloadDataCompletedEventHandler(request_DownloadDataCompleted);
request.DownloadDataAsync(new Uri(folder + file));
while (request.IsBusy) ;
....
static void request_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
if (e.TotalBytesToReceive == -1)
{
l.reportProgress(-1, FormatBytes(e.BytesReceived) + " out of ?" );
}
else
{
l.reportProgress(e.ProgressPercentage, "Downloaded " + FormatBytes(e.BytesReceived) + " out of " + FormatBytes(e.TotalBytesToReceive) + " (" + e.ProgressPercentage + "%)");
}
}
....
class FTPClient : WebClient
{
protected override WebRequest GetWebRequest(System.Uri address)
{
FtpWebRequest req = (FtpWebRequest)base.GetWebRequest(address);
req.UsePassive = false;
return req;
}
}
Thanks.

So I had the same issue. I got around it by retrieving the file size first.
// Get the object used to communicate with the server.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("URL");
request.Method = WebRequestMethods.Ftp.GetFileSize;
request.Credentials = networkCredential;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
bytes_total = response.ContentLength; //this is an int member variable stored for later
Console.WriteLine("Fetch Complete, ContentLength {0}", response.ContentLength);
response.Close();
webClient = new MyWebClient();
webClient.Credentials = networkCredential; ;
webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(FTPDownloadCompleted);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(FTPDownloadProgressChanged);
webClient.DownloadDataAsync(new Uri("URL"));
Then do the math in the callback.
private void FTPDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar.Value = (int)(((float)e.BytesReceived / (float)bytes_total) * 100.0);
}

With FTP protocol, WebClient in general does not know total download size. So you commonly get -1 with FTP.
Note that the behavior actually contradicts the .NET documentation, which says for FtpWebResponse.ContentLength (where the value of TotalBytesToReceive comes from):
For requests that use the DownloadFile method, the property is greater than zero if the downloaded file contained data and is zero if it was empty.
But you will easily find out many of questions about this, effectively showing that the behavior is not always as documented. The FtpWebResponse.ContentLength has a meaningful value for GetFileSize method only.
The FtpWebRequest/WebClient makes no explicit attempt to find out a size of the file that it is downloading. All it does is that it tries to look for (xxx bytes). string in 125/150 responses to RETR command. No FTP RFC mandates that the server should include such information. ProFTPD (see data_pasv_open in src/data.c) and vsftpd (see handle_retr in postlogin.c) seem to include this information. Other common FTP servers (IIS, FileZilla) do not do this.
If your server does not provide size information, you have to query for size yourself before download. A complete solution using FtpWebRequest and Task:
private void button1_Click(object sender, EventArgs e)
{
// Run Download on background thread
Task.Run(() => Download());
}
private void Download()
{
try
{
const string url = "ftp://ftp.example.com/remote/path/file.zip";
NetworkCredential credentials = new NetworkCredential("username", "password");
// Query size of the file to be downloaded
WebRequest sizeRequest = WebRequest.Create(url);
sizeRequest.Credentials = credentials;
sizeRequest.Method = WebRequestMethods.Ftp.GetFileSize;
int size = (int)sizeRequest.GetResponse().ContentLength;
progressBar1.Invoke(
(MethodInvoker)(() => progressBar1.Maximum = size));
// Download the file
WebRequest request = WebRequest.Create(url);
request.Credentials = credentials;
request.Method = WebRequestMethods.Ftp.DownloadFile;
using (Stream ftpStream = request.GetResponse().GetResponseStream())
using (Stream fileStream = File.Create(#"C:\local\path\file.zip"))
{
byte[] buffer = new byte[10240];
int read;
while ((read = ftpStream.Read(buffer, 0, buffer.Length)) > 0)
{
fileStream.Write(buffer, 0, read);
int position = (int)fileStream.Position;
progressBar1.Invoke(
(MethodInvoker)(() => progressBar1.Value = position));
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
The core download code is based on:
Upload and download a binary file to/from FTP server in C#/.NET

FTP Wont give you content sizes like HTTP does, you would probably be better doing this on your own.
FtpWebRequest FTPWbReq = WebRequest.Create("somefile") as FtpWebRequest;
FTPWbReq .Method = WebRequestMethods.Ftp.GetFileSize;
FtpWebResponse FTPWebRes = FTPWbReq.GetResponse() as FtpWebResponse;
long length = FTPWebRes.ContentLength;
FTPWebRes.Close();

Related

Download a URL image to a client PC via C#

I'm trying to access a file on the server via an api that is behind Basic Auth. I then want to download that to a client's PC.
I've got the following code which does GET the url from behind the basic auth, however the image never downloads properly. I either get a failed network error message or I get a message saying I can't download it because my machine doesn't have an app installed to open it. It's a png so it definitely does!
It goes the whole way through the code and doesn't error so I'm confused as to why it's not downloading correctly to the clients machine (my pc while I'm testing!)
In the code I am specifying one file and I have specified it's length as bytes just to try and narrow down where I'm going wrong. Normally this could be any file that's being access of any length!
This is the code I have:
//Create a stream for the file
Stream stream = null;
var size = fileResp.ContentLength; //I used this to determine the file was 64196 in size
//This controls how many bytes to read at a time and send to the client
int bytesToRead = 64196;
// Buffer to read bytes in chunk size specified above
byte[] buffer = new Byte[bytesToRead];
string url= "https://myURL/images/image-2019-04-02-16-25-18-458.png";
WebRequest myReq = WebRequest.Create(url);
string credentials = "username:pwd";
CredentialCache mycache = new CredentialCache();
myReq.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials));
myReq.Method = "GET";
// The number of bytes read
try
{
//Create a response for this request
HttpWebResponse fileResp = (HttpWebResponse)myReq.GetResponse();
if (myReq.ContentLength > 0)
fileResp.ContentLength = myReq.ContentLength;
//Get the Stream returned from the response
stream = fileResp.GetResponseStream();
// prepare the response to the client. resp is the client Response
var resp = HttpContext.Current.Response;
//Indicate the type of data being sent
string contentType = MimeMapping.GetMimeMapping("new.png");
resp.ContentType = contentType;
string fileName = "new.png";
//Name the file
resp.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
resp.AddHeader("Content-Length", fileResp.ContentLength.ToString());
int length;
do
{
// Verify that the client is connected.
if (resp.IsClientConnected)
{
// Read data into the buffer.
length = stream.Read(buffer, 0, bytesToRead);
// and write it out to the response's output stream
resp.OutputStream.Write(buffer, 0, length);
// Flush the data
resp.Flush();
//Clear the buffer
buffer = new Byte[bytesToRead];
}
else
{
// cancel the download if client has disconnected
length = -1;
}
} while (length > 0); //Repeat until no data is read
}
finally
{
if (stream != null)
{
//Close the input stream
stream.Close();
}
}
The output from here: fileResp.GetResponseStream();
At first, please try to test if the following code works on your computer.
private bool DownloadImage(string imgurl, string filename)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(imgurl);
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch
{
response = null;
return false;
}
if (response != null && response.StatusCode == HttpStatusCode.OK)
{
Stream receiveStream = response.GetResponseStream();
Bitmap bitmap = new Bitmap(receiveStream);
if (bitmap != null)
{
bitmap.Save(filename);
}
receiveStream.Flush();
receiveStream.Close();
response.Close();
return true;
}
else
{
return false;
}
}
private void button1_Click(object sender, EventArgs e)
{
string imgurl = "https://upload.wikimedia.org/wikipedia/commons/4/42/Cute-Ball-Go-icon.png";
string filename = "D:\\download_test.png";
bool bIsDownloadSuccess = DownloadImage(imgurl, filename);
}
This code works on me well.
It doesn't have error, but returns false.
Please check where false is returned.
If it has some error, the problem will be on Windows System.
Please try and let me know.
Thanks.

Resuming download in C# WPF

I’m developing a WPF application which downloads some MSI files which are greater than 100 MB. While downloading, if the internet is disconnected, the currently downloading file has to resume from where the download was interrupted. I used the WebClient and Cookies for downloading the files. The files are not resumed if the internet was disconnected and connected again. I have used the following code. Can anyone suggest me how to achieve the resume process?
using (CookieAwareWebClient client = new CookieAwareWebClient())
{
client.DownloadProgressChanged += WebClientDownloadProgressChanged;
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Client_DownloadFileCompleted);
client.DownloadFileAsync(url, fileName);
}
static void WebClientDownloadProgressChanged(object sender,
DownloadProgressChangedEventArgs e)
{
Console.WriteLine("Download status: {0}%.", e.ProgressPercentage);
Console.WriteLine(e.BytesReceived.ToString());
}
static void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
Console.WriteLine("Download finished!");
}
}
public class CookieAwareWebClient : WebClient
{
private readonly CookieContainer m_container = new CookieContainer();
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
HttpWebRequest webRequest = request as HttpWebRequest;
if (webRequest != null)
{
webRequest.CookieContainer = m_container;
}
return request;
}
}
For resuming the download, you can establish the connection once again und specify the offset of the data to be downloaded. Moreover, you can also request a part of the data to be downloaded.
// establishing the connection
HttpWebRequest oHttpWebRequest = (HttpWebRequest) WebRequest.Create ("myHttpAddress");
#if partialDownload
// reading the range 1000-2000 of the data
oHttpWebRequest.AddRange (1000, 2000);
// creating the file
FileInfo oFileInfo = new FileInfo ("myFilename");
FileStream oFileStream = oFileInfo.Create ();
#else
// reading after the last received byte
FileInfo oFileInfo = new FileInfo ("myFilename");
oHttpWebRequest.AddRange (oFileInfo.Length);
// opening the file for appending the data
FileStream oFileStream = File.Open (oFileInfo.FullName, FileMode.Append);
#endif
// opening the connection
HttpWebResponse oHttpWebResponse = (HttpWebResponse) oHttpWebRequest.GetResponse ();
Stream oReceiveStream = oHttpWebResponse.GetResponseStream ();
// reading the HTML stream and writing into the file
byte [] abBuffer = new byte [1000000];
int iReceived = oReceiveStream.Read (abBuffer, 0, abBuffer.Length);
while ( iReceived > 0 )
{
oFileStream.Write (abBuffer, 0, iReceived);
iReceived = oReceiveStream.Read (abBuffer, 0, abBuffer.Length);
};
// closing and disposing the resources
oFileStream .Close ();
oFileStream .Dispose ();
oReceiveStream .Close ();
oReceiveStream .Dispose ();
oHttpWebResponse.Close ();
oHttpWebResponse.Dispose ();
For debugging, you can take a look into the received data by looking into the returned header of the web request. For a partial download you might obtain for example
Content-Range: bytes 1000-2000/1156774
Content-Length: 1001
You obtain the keys of the header from
oHttpWebResponse.Headers.Keys []
and the values from
oHttpWebResponse.Headers []

Progress in uploading in ftp server c#

I ve got the following code which
foreach (string dir in dirs) { //dirs all files in directory
try{
// Get an instance of WebClient
WebClient client = new System.Net.WebClient();
// parse the ftp host and file into a uri path for the upload
Uri uri = new Uri(m_FtpHost + new FileInfo(dir).Name);
// set the username and password for the FTP server
client.Credentials = new System.Net.NetworkCredential(m_FtpUsername, m_FtpPassword);
// upload the file asynchronously, non-blocking.
client.UploadFileAsync(uri, "STOR",dir);
}
catch(Exception e){
print(e.Message);
}
}
Can I retrieve back the progress of the upload? I have in the dirs 4-5 files. I want exact the progress (not the files uploaded/(total files))
EDIT: Thus the right approach is the following:
public int percentage;
try{
// Get an instance of WebClient
WebClient client = new System.Net.WebClient();
// parse the ftp host and file into a uri path for the upload
Uri uri = new Uri(m_FtpHost + new FileInfo(dir).Name);
// set the username and password for the FTP server
client.Credentials = new System.Net.NetworkCredential(m_FtpUsername, m_FtpPassword);
// upload the file asynchronously, non-blocking.
client.UploadProgressChanged += WebClientUploadProgressChanged;
client.UploadFileCompleted += WebClientUploadCompleted;
client.UploadFileAsync(uri, "STOR",dir);
}
void WebClientUploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
percentage = e.ProgressPercentage;
}
void WebClientUploadCompleted(object sender, UploadFileCompletedEventArgs e)
{
print( "Upload is finished. ");
}
I add this implementation to my code, however it seems that it doenst print anything in the console.
WebClient contains a dedicated event for this
public event UploadProgressChangedEventHandler UploadProgressChanged
https://msdn.microsoft.com/en-us/library/system.net.webclient.uploadprogresschanged(v=vs.110).aspx
EDIT : HttpWebRequest approach based on a google result :
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "text/plain";
request.Timeout = -1; //Infinite wait for the response.
// Get the file information object.
FileInfo fileInfo = new FileInfo("C:\\Test\\uploadFile.dat");
// Set the file content length.
request.ContentLength = fileInfo.Length;
// Get the number of segments the file will be uploaded to the stream.
int segments = Convert.ToInt32(fileInfo.Length / (1024 * 4));
// Get the source file stream.
using (FileStream fileStream = fileInfo.OpenRead())
{
// Create 4KB buffer which is file page size.
byte[] tempBuffer = new byte[1024 * 4];
int bytesRead = 0;
// Write the source data to the network stream.
using (Stream requestStream = request.GetRequestStream())
{
// Loop till the file content is read completely.
while ((bytesRead = fileStream.Read(tempBuffer, 0, tempBuffer.Length)) > 0)
{
// Write the 4 KB data in the buffer to the network stream.
requestStream.Write(tempBuffer, 0, bytesRead);
// Update your progress bar here using segment count.
}
}
}
// Post the request and Get the response from the server.
using (WebResponse response = request.GetResponse())
{
// Request is successfully posted to the server.
}

Uploading HTTP progress tracking

I've got WPF application I'm writing that posts files to one of social networks.
Upload itself working just fine, but I'd like to provide some indication of how far along I am with the uploading.
I tried a bunch of ways to do this:
1) HttpWebRequest.GetStream method:
using (
var FS = File.Open(
localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
long len = FS.Length;
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "POST";
request.ProtocolVersion = HttpVersion.Version11;
request.ContentType = "multipart/form-data; boundary=--AaB03x";
//predata and postdata is two byte[] arrays, that contains
//strings for MIME file upload (defined above and is not important)
request.ContentLength = predata.Length + FS.Length + postdata.Length;
request.AllowWriteStreamBuffering = false;
using (var reqStream = request.GetRequestStream())
{
reqStream.Write(predata, 0, predata.Length);
int bytesRead = 0;
int totalRead = 0;
do
{
bytesRead = FS.Read(fileData, 0, MaxContentSize);
totalRead += bytesRead;
reqStream.Write(fileData, 0, bytesRead);
reqStream.Flush(); //trying with and without this
//this part will show progress in percents
sop.prct = (int) ((100*totalRead)/len);
} while (bytesRead > 0);
reqStream.Write(postdata, 0, postdata.Length);
}
HttpWebResponse responce = (HttpWebResponse) request.GetResponse();
using (var respStream = responce.GetResponseStream())
{
//do things
}
}
2) WebClient way (much shorter):
void UploadFile (url, localFilePath)
{
...
WebClient client = new WebClient();
client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadPartDone);
client.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadComplete);
client.UploadFileAsync(new Uri(url), localFilePath);
done.WaitOne();
//do things with responce, received from UploadComplete
JavaScriptSerializer jssSer = new JavaScriptSerializer();
return jssSer.Deserialize<UniversalJSONAnswer>(utf8.GetString(UploadFileResponce));
//so on...
...
}
void UploadComplete(object sender, UploadFileCompletedEventArgs e)
{
UploadFileResponce=e.Result;
done.Set();
}
void UploadPartDone(object sender, UploadProgressChangedEventArgs e)
{
//this part expected to show progress
sop.prct=(int)(100*e.BytesSent/e.TotalBytesToSend);
}
3) Even TcpClient way:
using (
var FS = File.Open(
localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
long len = FS.Length;
long totalRead = 0;
using (var client = new TcpClient(urli.Host, urli.Port))
{
using (var clearstream = client.GetStream())
{
using (var writer = new StreamWriter(clearstream))
using (var reader = new StreamReader(clearstream))
{
//set progress to 0
sop.prct = 0;
// Send request headers
writer.WriteLine("POST " + urli.AbsoluteUri + " HTTP/1.1");
writer.WriteLine("Content-Type: multipart/form-data; boundary=--AaB03x");
writer.WriteLine("Host: " + urli.Host);
writer.WriteLine("Content-Length: " + (predata.Length + len + postdata.Length).ToString());
writer.WriteLine();
//some data for MIME
writer.Write(utf8.GetString(predata));
writer.Flush();
int bytesRead;
do
{
bytesRead = FS.Read(fileData, 0, MaxContentSize);
totalRead += bytesRead;
writer.BaseStream.Write(fileData, 0, bytesRead);
writer.BaseStream.Flush();
sop.prct = (int) ((100*totalRead)/len);
} while (bytesRead > 0)
writer.Write(utf8.GetString(postdata));
writer.Flush();
//read line of response and do other thigs...
respStr = reader.ReadLine();
...
}
}
}
}
In all cases the file was successfully sent to the server.
But always progress looks like this: for a few seconds it runs from 0 to 100 and then waits until file actually uploading (about 5 minutes - file is 400MB).
So I think the data from a file is buffered somewhere and I'm tracking not uploading, but buffering data. And then must wait until it's uploaded.
My questions are:
1) Is there any way to track actual uploading data? That the method Stream.Write() or Flush() (which as I read somewhere, does not work for NetworkStream) did not return until it receives confirmation from the server that the TCP packets received.
2) Or can I deny buffering (AllowWriteStreamBUffering for HttpWebRequest doesn't work)?
3) And does it make sense to go further "down" and try with Sockets?
updated:
To avoid any doubts in the way of progress displaying on UI, I rewrote the code to log a file.
so, here is code:
using (var LogStream=File.Open("C:\\123.txt",FileMode.Create,FileAccess.Write,FileShare.Read))
using (var LogWriter=new StreamWriter(LogStream))
using (var FS = File.Open(localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
long len = FS.Length;
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Timeout = 7200000; //2 hour timeout
request.Method = "POST";
request.ProtocolVersion = HttpVersion.Version11;
request.ContentType = "multipart/form-data; boundary=--AaB03x";
//predata and postdata is two byte[] arrays, that contains
//strings for MIME file upload (defined above and is not important)
request.ContentLength = predata.Length + FS.Length + postdata.Length;
request.AllowWriteStreamBuffering = false;
LogWriter.WriteLine(DateTime.Now.ToString("o") + " Start write into request stream. ");
using (var reqStream = request.GetRequestStream())
{
reqStream.Write(predata, 0, predata.Length);
int bytesRead = 0;
int totalRead = 0;
do
{
bytesRead = FS.Read(fileData, 0, MaxContentSize);
totalRead += bytesRead;
reqStream.Write(fileData, 0, bytesRead);
reqStream.Flush(); //trying with and without this
//sop.prct = (int) ((100*totalRead)/len); //this part will show progress in percents
LogWriter.WriteLine(DateTime.Now.ToString("o") + " totalRead= " + totalRead.ToString() + " / " + len.ToString());
} while (bytesRead > 0);
reqStream.Write(postdata, 0, postdata.Length);
}
LogWriter.WriteLine(DateTime.Now.ToString("o") + " All sent!!! Waiting for responce... ");
LogWriter.Flush();
HttpWebResponse responce = (HttpWebResponse) request.GetResponse();
LogWriter.WriteLine(DateTime.Now.ToString("o") + " Responce received! ");
using (var respStream = responce.GetResponseStream())
{
if (respStream == null) return null;
using (var streamReader = new StreamReader(respStream))
{
string resp = streamReader.ReadToEnd();
JavaScriptSerializer jssSer = new JavaScriptSerializer();
return jssSer.Deserialize<UniversalJSONAnswer>(resp);
}
}
}
and here is result (I cut the middle):
2011-11-19T22:00:54.5964408+04:00 Start write into request stream.
2011-11-19T22:00:54.6404433+04:00 totalRead= 1048576 / 410746880
2011-11-19T22:00:54.6424434+04:00 totalRead= 2097152 / 410746880
2011-11-19T22:00:54.6434435+04:00 totalRead= 3145728 / 410746880
2011-11-19T22:00:54.6454436+04:00 totalRead= 4194304 / 410746880
2011-11-19T22:00:54.6464437+04:00 totalRead= 5242880 / 410746880
2011-11-19T22:00:54.6494438+04:00 totalRead= 6291456 / 410746880
.......
2011-11-19T22:00:55.3434835+04:00 totalRead= 408944640 / 410746880
2011-11-19T22:00:55.3434835+04:00 totalRead= 409993216 / 410746880
2011-11-19T22:00:55.3464837+04:00 totalRead= 410746880 / 410746880
2011-11-19T22:00:55.3464837+04:00 totalRead= 410746880 / 410746880
2011-11-19T22:00:55.3464837+04:00 All sent!!! Waiting for responce...
2011-11-19T22:07:23.0616597+04:00 Responce received!
as you can see program thinks that it uploaded ~400MB for about 2 seconds. And after 7 minutes file actually uploads and I receive responce.
updated again:
Seems to this is happening under WIndows 7 (not shure about x64 or x86).
When I run my code uder XP everything works perfectly and progress is shown absolute correctly
it's more than year since this question was posted, but I think my post can be usefull for someone.
I had the same problem with showing progress and it behaved exactly like you described. So i decided to use HttpClient which shows upload progress correctly. Then I've encountered interesting bug - when I had Fiddler launched HttpClient started to show its upload progress in unexpected way like in WebClient/HttpWebRequest above so I thinked maybe that was a problem of why WebClient showed upload progres not correctly (I think I had it launched). So I tried with WebClient again (without fiddler-like apps launched) and all works as it should, upload progress has correct values. I have tested in on several PC with win7 and XP and in all cases progress was showing correctly.
So, I think that such program like Fiddler (probably not only a fiddler) has some affect on how WebClient and other .net classes shows upload progress.
this discussion approves it:
HttpWebRequest doesn't work except when fiddler is running
You could use the WebClient's UploadFile to upload file rather than using writing file as a file stream. In order to track the percentage of the data received and uploaded you can use UploadFileAsyn and subscribe to its events.
In the code bellow I've used UploadFileAsyn to the upload files synchronously, but it need not to be synchronous as far as you don't dispose the instance of the uploader.
class FileUploader : IDisposable
{
private readonly WebClient _client;
private readonly Uri _address;
private readonly string _filePath;
private bool _uploadCompleted;
private bool _uploadStarted;
private bool _status;
public FileUploader(string address, string filePath)
{
_client = new WebClient();
_address = new Uri(address);
_filePath = filePath;
_client.UploadProgressChanged += FileUploadProgressChanged;
_client.UploadFileCompleted += FileUploadFileCompleted;
}
private void FileUploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
{
_status = (e.Cancelled || e.Error == null) ? false : true;
_uploadCompleted = true;
}
private void FileUploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
if(e.ProgressPercentage % 10 == 0)
{
//This writes the pecentage data uploaded and downloaded
Console.WriteLine("Send: {0}, Received: {1}", e.BytesSent, e.BytesReceived);
//You can have a delegate or a call back to update your UI about the percentage uploaded
//If you don't have the condition (i.e e.ProgressPercentage % 10 == 0 )for the pecentage of the process
//the callback will slow you upload process down
}
}
public bool Upload()
{
if (!_uploadStarted)
{
_uploadStarted = true;
_client.UploadFileAsync(_address, _filePath);
}
while (!_uploadCompleted)
{
Thread.Sleep(1000);
}
return _status;
}
public void Dispose()
{
_client.Dispose();
}
}
Client Code:
using (FileUploader uploader = new FileUploader("http://www.google.com", #"C:\test.txt"))
{
uploader.Upload();
}
You can register a custom callback (may be a delegate) on the FileUploadProgressChanged event handler to update your WPF UI.
The upload progress changed event get called more often if your callback for the event does any IO then that'll slowdown the download progress. It's best to have infrequent update e.g. the following code update only evey 10% up.
private int _percentageDownloaded;
private void FileUploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
if (e.ProgressPercentage % 10 == 0 && e.ProgressPercentage > _percentageDownloaded)
{
_percentageDownloaded = e.ProgressPercentage;
//Any callback instead of printline
Console.WriteLine("Send: {0} Received: {1}", e.BytesSent, e.BytesReceived);
}
}
my suggestion is to use new HTTPClient class (available in .NET 4.5). It supports progress.
This article helped me a lot with this:
http://www.strathweb.com/2012/06/drag-and-drop-files-to-wpf-application-and-asynchronously-upload-to-asp-net-web-api/
My code for upload file:
private void HttpSendProgress(object sender, HttpProgressEventArgs e)
{
HttpRequestMessage request = sender as HttpRequestMessage;
Console.WriteLine(e.BytesTransferred);
}
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
ProgressMessageHandler progress = new ProgressMessageHandler();
progress.HttpSendProgress += new EventHandler<HttpProgressEventArgs>(HttpSendProgress);
HttpRequestMessage message = new HttpRequestMessage();
StreamContent streamContent = new StreamContent(new FileStream("e:\\somefile.zip", FileMode.Open));
message.Method = HttpMethod.Put;
message.Content = streamContent;
message.RequestUri = new Uri("{Here your link}");
var client = HttpClientFactory.Create(progress);
client.SendAsync(message).ContinueWith(task =>
{
if (task.Result.IsSuccessStatusCode)
{
}
});
}
This one has been bugging me for at least one day. I have started with using WebClient.UploadFileAsync, next tried the ProgressMessageHandler for HttpClient then rolled my own HttpContent for the HttpClient API. None of those approaches worked (for me).
It appears HttpWebRequest, which sits at the bottom of most (all?) .NET Http abstraction like WebClient and HttpClient, buffers the request and response stream by default, which I confirmed by looking at it in ILSpy.
As others have noted, you can make your request use chunked encoding one way or another which will effectively disable buffering the request stream, but still this is not going to fix the progress reporting.
I found that it was necessary to flush the request stream after each block that I send in order to accurately reflect sending progress, or else your data will simply be buffered one step further down the pipeline (probably somewhere in NetworkStream or OS, didn't check). The sample code below works for me and also does a minimalistic job at translating back from a HttpWebResponse to HttpResponseMessage (which you may not need, YMMV).
public async Task<HttpResponseMessage> UploadFileAsync( string uploadUrl, string absoluteFilePath, Action<int> progressPercentCallback )
{
var length = new FileInfo( absoluteFilePath ).Length;
var request = new HttpWebRequest( new Uri(uploadUrl) ) {
Method = "PUT",
AllowWriteStreamBuffering = false,
AllowReadStreamBuffering = false,
ContentLength = length
};
const int chunkSize = 4096;
var buffer = new byte[chunkSize];
using (var req = await request.GetRequestStreamAsync())
using (var readStream = File.OpenRead(absoluteFilePath))
{
progressPercentCallback(0);
int read = 0;
for (int i = 0; i < length; i += read)
{
read = await readStream.ReadAsync( buffer, 0, chunkSize );
await req.WriteAsync( buffer, 0, read );
await req.FlushAsync(); // flushing is required or else we jump to 100% very fast
progressPercentCallback((int)(100.0 * i / length));
}
progressPercentCallback(100);
}
var response = (HttpWebResponse)await request.GetResponseAsync();
var result = new HttpResponseMessage( response.StatusCode );
result.Content = new StreamContent( response.GetResponseStream() );
return result;
}
At fast guess, you are running this code on UI thread. You need to run upload stuff on new thread.
At that point you have 2 options. 1) You run timer on UI thread and update UI. 2) You update UI using Invoke(because you can't access UI from another thread) calls to update UI.
In the first example I think your progress bar is showing how fast you write into the stream from the file on disk - not the actual upload progress (which is why it all happens to 100% really quickly then the upload chugs on*).
I might be wrong ^^ and have no WPF experience but I have uploaded massive files from Silverlight to WCF and the model used there is (as you do) to break up the file into blocks. Send each block. When you get a response from the server ("block 26 received ok"), update the progress bar as really, you can't (or should not) update the progress bar unless you /know/ that block x made it - and a good way to know that is if the server says it got it.
*I wish I could upload 400Mb in 5 mins. Would take me all day...
I had the same problem. I spent a lot of time and solved the problem as follows:
Antivirus AVAST. When I turn it off my program works perfectly...

Uploading a text box into a text file on an ftp C#

I am trying to stream a multiline textbox into a text file on an ftp server. Can someone tell me where I may be going wrong?
private void btnSave_Click(object sender, EventArgs e)
{
UriBuilder b = new UriBuilder();
b.Host = "ftp.myserver.com";
b.UserName = "user";
b.Password = "pass";
b.Port = 21;
b.Path = "/myserver.com/directories/" + selected + ".txt";
b.Scheme = Uri.UriSchemeFtp;
Uri g = b.Uri;
System.Net.FtpWebRequest c = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create(g);
c.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
System.Net.FtpWebResponse d = (System.Net.FtpWebResponse)c.GetResponse();
System.IO.Stream h = d.GetResponseStream;
System.IO.StreamWriter SW = new System.IO.StreamWriter(h);
String[] contents = textBox1.Lines.ToArray();
for (int i = 0; i < contents.Length; i++)
{
SW.WriteLine(contents[i]);
}
h.Close();
SW.Close();
d.Close();
}
The error I am getting is this line:
System.IO.StreamWriter SW = new System.IO.StreamWriter(h);
Stream was not writable.
Any ideas?
The response stream from an FTP site is data from the site to you. You'd need the request stream... but then you wouldn't want a method of DownloadFile - you're not downloading, you're uploading, so you want the UploadFile method.
Additionally:
You're not closing anything if exceptions are thrown: use using blocks for this.
It's a bad idea to do network access like this on the UI thread; the UI thread will block (so the whole UI will hang) while the FTP request is happening. Use a background thread instead.
To upload a file you need to use the FtpWebRequest class.
Quote:
When using an FtpWebRequest object to
upload a file to a server, you must
write the file content to the request
stream obtained by calling the
GetRequestStream method or its
asynchronous counterparts, the
BeginGetRequestStream and
EndGetRequestStream methods. You must
write to the stream and close the
stream before sending the request.
For an example of uploading a file (which you can change to writing stream content as in your example) see here.
Taken from MSDN and slightly modified:
public static bool UploadFileOnServer(string fileName, Uri serverUri)
{
// The URI described by serverUri should use the ftp:// scheme.
// It contains the name of the file on the server.
// Example: ftp://contoso.com/someFile.txt.
// The fileName parameter identifies the file
// to be uploaded to the server.
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return false;
}
// Get the object used to communicate with the server.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(serverUri);
request.Method = WebRequestMethods.Ftp.UploadFile;
StreamReader sourceStream = new StreamReader(fileName);
byte [] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
sourceStream.Close();
request.ContentLength = fileContents.Length;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential ("anonymous","janeDoe#contoso.com");
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileContents, 0, fileContents.Length);
requestStream.Close();
FtpWebResponse response = (FtpWebResponse) request.GetResponse();
Console.WriteLine("Upload status: {0}",response.StatusDescription);
response.Close();
return true;
}

Categories