Unity, WebClient, Upload, Async and Threads.
Hi there everyone. This is rather a question for .NET experts.
OS: Irrelevant, editor script. But Editor works on Windows 10 for this case.
Unity version: 2017.1
IDE: MonoDevelop
Purpose: An editor window script which selects a folder, then compresses it and uploads it to a server via FTP. Upload is the part in question.
State:
I am currently using WebClient.UploadDataAsync to upload a file using FTP credentials. I have two problems which actually come down to one arch-problem. Upon intense research for weeks, I've understood one thing: There's simply no way of checking progress properly and no way of closing the FTP connection once it's opened even if it hangs for a reason which is quite a problem since I have no way of canceling the upload. Getting file size works. Static Coroutine also works. OnFileUploadCompleted fires but only if upload is really completed. OnUploadProgressChanged never fires; Tried zillions of alternatives. Disposed fires but ftp connection does not get closed. CancelAsync() also does nothing.
What happens:
When client.UploadDataAsync(uri, "STOR", bytes); triggers, file(data) upload starts peacefully, getting the size works properly and everything is fine. But after a time, upload just hangs. Simply hangs. No exception throw, no event trigger, nothing. Just stays there forever. I even check my server for FTP connections and it shows active connection with session named UL. Also Getting file size shows total size correct, uploaded size correct also. I check from FileZilla to see and the file is created correctly, named correctly and uploads to a certain part too. Unfortunately that hang occurs at random...
Question:
1. What could I do in order to track progress? I've come up with a way of checking file size with FtpWebRequest every 1-2 seconds but even for an editor script for a developer, that feels like an overkill.
2. How can i check if a timeout occurred? Custom webclient timeout setting does not work it seems. Or is there another way to do it? Could that be server related?
3. How can I completely abort,dispose,kill,murder,destroy,annihilate,disintegrate or obliterate the FTP connection created by the WebClient? :) Sending to dark oblivion is fine too. There seems to be literally no way to access it.
4. I definitely know that is not the greatest way to do it but since this will just work in the editor, I assume using the easier and thread-safe way is OK?
Code: (The complete file is too long, just pasting the relevant part. All using statements are correct. All required variables are pre-defined.)
public static void UploadFile()
{
Debug.Log("Path: " + FilePath);
Debug.Log("Username: " + FTPUserName);
Debug.Log("Pass: " + FTPPassword);
Debug.Log("Host: ftp://" + FTPHost);
Uri uri = new Uri("ftp://"+FTPHost + new FileInfo(FilePath).Name);
client.Credentials = new System.Net.NetworkCredential(FTPUserName, FTPPassword);
client.UploadDataCompleted += new UploadDataCompletedEventHandler(OnFileUploadCompleted);
client.UploadProgressChanged += (object sender, UploadProgressChangedEventArgs e) => {Debug.Log(e.ProgressPercentage);};
client.Disposed += (sender, e) => {Debug.Log("Disposed of uploader client");};
client.Timeout = 2000;
byte[] bytes = File.ReadAllBytes (FilePath);
client.UploadDataAsync(uri, "STOR", bytes);
EditorLogGenerator.GenerateLog ("Upload started");
shouldCheck = true;
StaticCoroutine.DoCoroutine (readSize ());
Uri uri2 = new Uri("ftp://"+FTPHost + "version.flamacore");
client2.Credentials = new System.Net.NetworkCredential(FTPUserName, FTPPassword);
client2.UploadFileCompleted += new UploadFileCompletedEventHandler(OnFileUploadCompleted2);
client2.UploadFileAsync(uri2, "STOR", Application.dataPath + "/../TheLauncher/version/version.flamacore");
EditorLogGenerator.GenerateLog ("Version File Upload Started");
Uri uri3 = new Uri("ftp://"+FTPHost + "v.flamacore");
client3.Credentials = new System.Net.NetworkCredential(FTPUserName, FTPPassword);
client3.UploadDataCompleted += new UploadDataCompletedEventHandler(OnFileUploadCompleted3);
byte[] bytes3 = File.ReadAllBytes (Application.dataPath + "/../TheLauncher/version/v.flamacore");
client3.UploadDataAsync(uri3, "STOR", bytes3);
EditorLogGenerator.GenerateLog ("File List Upload Started");
}
public static IEnumerator readSize()
{
yield return new WaitForSeconds (0.50f);
while (shouldCheck) {
t1_ = new Thread (() => {
var ftpWebRequest = (FtpWebRequest)WebRequest.Create(new Uri ("ftp://" + FTPHost + new FileInfo (FilePath).Name)); //Create FtpWebRequest with given Request Uri.
ftpWebRequest.Credentials = new NetworkCredential (FTPUserName, FTPPassword); //Set the Credentials of current FtpWebRequest
ftpWebRequest.Method = WebRequestMethods.Ftp.GetFileSize; //Set the Method of FtpWebRequest incase it has a value.
long fileSize = ftpWebRequest.GetResponse().ContentLength;;
UploadStatus = "Upload Progress: " + GetBytesReadable(fileSize) + "/" + GetBytesReadable(new System.IO.FileInfo (FilePath).Length);
Debug.Log (UploadStatus);
});
if (!t1_.IsAlive)
t1_.Start ();
EditorLogGenerator.GenerateLog (t1_.Name + "Upload Progress Thread Started");
yield return new WaitForSeconds (1.5f);
}
}