I’m prototyping an ftp utility that will save a user’s progress, and re-upload a given file from where they started if their internet were to disconnect. (It’s for clients with slower connections).
The idea is to open a file stream and ftp stream, and write to the ftp stream in chunks of memory. If an exception rises (i.e. an IOException from a disconnect), then the number of bytes written to the ftp server will be saved in a log file and read on start up.
This code works great if the transaction is cancelled, but if the client is to disconnect, then the ftp stream is never cleaned up on the server end – so I receive…
The remote server returned an error: (550) File unavailable (e.g., file not found, no access).
When re-requesting the ftp stream for the given file. The code looks like
//Create FTP Web Request
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(builder.ToString()));
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
reqFTP.UsePassive = true;
reqFTP.UseBinary = false;
reqFTP.KeepAlive = false;
reqFTP.ContentLength = fileInf.Length;
reqFTP.ReadWriteTimeout = 5000;
reqFTP.Timeout = 5000;
using (ProgressDialog progressDialog = new ProgressDialog())
{
progressDialog.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
progressDialog.backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
progressDialog.backgroundWorker1.FileName = filename;
progressDialog.ShowDialog();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
FTPBackgroundWorker worker = sender as FTPBackgroundWorker;
//Get file progress (if user canceled or crashed)
worker.NumBytesRead = GetFileProgress(worker.FileName);
reqFTP.ContentOffset = worker.NumBytesRead;
const int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
using (worker.FileStream = fileInf.OpenRead())
{
worker.FileStream.Position = worker.NumBytesRead;
worker.FTPStream = reqFTP.GetRequestStream(); //Exception occurs
while (true)
{
bool throwException = false;
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
contentLen = worker.FileStream.Read(buff, 0, buffLength);
if (contentLen == 0)
break;
//write file to ftp stream
worker.FTPStream.Write(buff, 0, contentLen);
worker.NumBytesRead += contentLen;
//For testing purposes
if (throwException)
throw new Exception("user disconnected!");
worker.ReportProgress((int)(((double)worker.NumBytesRead / fileInf.Length) * 100));
}
worker.FileStream.Close();
worker.FTPStream.Close();
}
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
FTPBackgroundWorker worker = sender as FTPBackgroundWorker;
if (e.Error != null)
{
MessageBox.Show(String.Format("Error occured uploading {0}: {1}",worker.FileName, e.Error), "Error");
if (worker.FileStream != null)
worker.FileStream.Close();
if (worker.FTPStream != null)
worker.FTPStream.Close();
if(worker.NumBytesRead > 0)
{
MessageBox.Show("Progress has been saved", "Notification");
WriteToLogFile(worker.FileName, worker.NumBytesRead);
}
}
else if (e.Cancelled)
{
if (worker.FileStream != null)
worker.FileStream.Close();
if (worker.FTPStream != null)
worker.FTPStream.Close();
MessageBox.Show("Upload Canceled", "Cancel");
if (worker.NumBytesRead > 0 && MessageBox.Show("Would you like to save your upload progress?", "Notification", MessageBoxButtons.YesNo) == DialogResult.Yes)
WriteToLogFile(worker.FileName, worker.NumBytesRead);
}
else
{
RemoveFromLogFile(worker.FileName);
MessageBox.Show("Upload Complete", "Success");
}
}
My question is: Is there a way to check if there’s a handle on the server end that’s not letting go of a file path and remove it? Or am I approaching the problem with the wrong methodology?
Thanks
Why not decrement the timeout to a value that will expire before the connection tries to be initialized again or make the user wait to try to start the connection again?