/// <summary> /// Manages every aspect of the upload from start to finish. /// </summary> public void UploadFile() { //Create a timestamp for logging time spent DateTime Start = DateTime.Now; //Create a new upload details to hold the details of the upload uploadDetails = new UploadDetails(); //Start large file upload StartLargeFile(); //Upload the parts RunUploadWorkers(); //Finish Large File upload FinishLargeFile(); #region Output final status message. //Get end datetime DateTime End = DateTime.Now; //Get the difference between the two as a string string diffInSeconds = Math.Round((End - Start).TotalSeconds, 1).ToString(); //Get Mbps //First get MB var MBs = fileInfo.Length * 0.00000095367432; //Then mb var Mbs = MBs * 8; //Then calculate Mbps var Mbps = Mbs / (End - Start).TotalSeconds; StaticHelpers.DebugLogger($"Operation Finished. Operation Took: {diffInSeconds} seconds and transferred {Math.Round(MBs, 2)}MBs at a speed of {Math.Round(Mbps, 2)}Mbps", DebugLevel.Info); #endregion }
/// <summary> /// Starts and is the upload worker that does the actual uploads. /// </summary> public async void StartUploadWorker() { StaticHelpers.DebugLogger("Starting an internal upload worker", DebugLevel.Verbose); //Get our object containing our authorisation URLs UploadPartsUrlDetails uploadPartsUrlDetails = await GetUploadPartUrl(); #region Loop where work is done //While there are bytes still be sent while (uploadDetails.totalBytesSent < localFileSize) { #region Check Bandwidth Monitor //If the bandwidth monitor requires a reduction in usage if (bandwidthMonitor.reduceUsage || bandwidthMonitor.urgentReduceUsage) { //Check thread count is greater than 1 if (AllThreads.Count(thread => thread.ThreadState != System.Threading.ThreadState.Stopped) > 1) { //Log to debug StaticHelpers.DebugLogger("Received Kill Request from Bandwidth Monitor. Killing self....", DebugLevel.Verbose); //Set reduceUsage to false bandwidthMonitor.reduceUsage = false; //Kill this thread break; } else { StaticHelpers.DebugLogger("Received Kill Request from Bandwidth Monitor HOWEVER as the only remaining thread I am ignoring.", DebugLevel.Verbose); } } #endregion #region Get details from uploadDetails & Data from file //Create variables outside of the lock so we can set it inside but still access it outside //For a snapshot of uploadDetails UploadDetails uploadDetailsSnapshot = new UploadDetails(); //Create the byte array for the data we are going to use byte[] data = new byte[Singletons.options.PartSize * (1000 * 1000)]; //Lock the uploadDetails lock (uploadDetailsLock) { #region Check if we are on the last part or even if there are no parts left //If there is nothing left to upload if ((localFileSize - uploadDetails.totalBytesSent) == 0) { //Break out of the loop as there is no more work to do break; } //If the remaining bytes are less the minimum part size if ((localFileSize - uploadDetails.totalBytesSent) <= uploadDetails.minimumPartSize) { //Changes the bytes sent for part to the remaining number of bytes uploadDetails.bytesSentForPart = (localFileSize - uploadDetails.totalBytesSent); } #endregion #region Read & hash File // Generate SHA1 Chunk // Open stream of the file FileStream f = File.OpenRead(pathToFile); //Seek to the location in the file we are currently up to f.Seek(uploadDetails.totalBytesSent, SeekOrigin.Begin); //Read the data from the file that we are going to use this time f.Read(data, 0, (int)uploadDetails.bytesSentForPart); //Create a blank SHA1 hash SHA1 sha1 = SHA1.Create(); //Hash the bytes in our current data and keep the hash in hashData byte[] hashData = sha1.ComputeHash(data, 0, (int)uploadDetails.bytesSentForPart); //Dispose of the hash sha1.Dispose(); //Create a string builder to manipulate the hash StringBuilder sb = new StringBuilder(); //Add data to every byte in the range foreach (byte b in hashData) { sb.Append(b.ToString("x2")); } //Close the file read because we now have the data f.Close(); //Add the hash to the hash array uploadDetails.partSha1Array.Add(sb.ToString()); #endregion #region Finalise Operations on uploadDetails so we can release lock //Get all the values we might need to use internally. OR just make a snapshot of Upload Details? (Yes this should work!) uploadDetailsSnapshot = uploadDetails.CloneMe(); //Update the actual uploadDetails with what we intend to do. //Increment the partNo uploadDetails.partNo++; //Increment the totalBytesSent uploadDetails.totalBytesSent = uploadDetails.totalBytesSent + uploadDetails.bytesSentForPart; #endregion } #endregion //To count number of failed attempts int WebRequestAttempt = 1; //To allow retry of the failed request RetryPartUpload: //Output urls for debugging //StaticHelpers.DebugLogger("UploadPartsURL is: " + uploadPartsUrlDetails.uploadUrl + ". Key is: " + uploadPartsUrlDetails.authorizationToken, DebugLevel.FullDebug); //Start a new web request HttpWebRequest uploadPartRequest = (HttpWebRequest)WebRequest.Create(uploadPartsUrlDetails.uploadUrl); //Set to post uploadPartRequest.Method = "POST"; //Set the request timeout to 5 minutes uploadPartRequest.Timeout = 5 * 60 * 1000; //Set authorization token (using the one for the current uploadPartUrl) //Intentionally generating error: //uploadPartRequest.Headers.Add("Authorization", uploadPartsUrlDetails.authorizationToken + "r"); uploadPartRequest.Headers.Add("Authorization", uploadPartsUrlDetails.authorizationToken); //Set the part number uploadPartRequest.Headers.Add("X-Bz-Part-Number", uploadDetailsSnapshot.partNo.ToString()); //Set the sha1 hash from the array (minus one on the part number because 0-index array uploadPartRequest.Headers.Add("X-Bz-Content-Sha1", (String)uploadDetailsSnapshot.partSha1Array[(uploadDetailsSnapshot.partNo - 1)]); //Set content type to json uploadPartRequest.ContentType = "application/json; charset=utf-8"; //Set the content length to the bytes sent for the part uploadPartRequest.ContentLength = uploadDetailsSnapshot.bytesSentForPart; //Create a stream to use for the uploadPartRequest (this may be the one to change to a filestream) using (Stream stream = uploadPartRequest.GetRequestStream()) { //Write the data (through the stream?) to the uploadPartRequest stream.Write(data, 0, (int)uploadDetailsSnapshot.bytesSentForPart); //Close the stream stream.Close(); } //Set upload response to null HttpWebResponse uploadPartResponse = null; //Verbose message StaticHelpers.DebugLogger("Starting upload of part " + uploadDetailsSnapshot.partNo, DebugLevel.Verbose); //Try the upload try { //Try the upload and set the upload part response to the response uploadPartResponse = (HttpWebResponse)uploadPartRequest.GetResponse(); } //If theres an exception catch and output it catch (WebException e) { if (e.Response == null) { StaticHelpers.DebugLogger("Upload has failed with error: " + e.Message, DebugLevel.Warn); } else { using (WebResponse r = e.Response) { HttpWebResponse httpResponse = (HttpWebResponse)r; StaticHelpers.DebugLogger($"Internal Worker Error with API.Error code: {httpResponse.StatusCode}. Retrying....", DebugLevel.Warn); using (Stream dataE = r.GetResponseStream()) using (var reader = new StreamReader(dataE)) { string text = reader.ReadToEnd(); StaticHelpers.DebugLogger($"Internal Worker Error with API.Error code: {text}. Retrying....", DebugLevel.Warn); } } } //If we have failed less than 5 times if (WebRequestAttempt < 5) { //Log a message StaticHelpers.DebugLogger("Upload has failed, getting fresh uploadparturl and retrying....", DebugLevel.Verbose); //Get our object containing our authorisation URLs uploadPartsUrlDetails = await GetUploadPartUrl(); //Output fresh url for debugging StaticHelpers.DebugLogger("Fresh UploadPartsURL is: " + uploadPartsUrlDetails.uploadUrl + ". Key is: " + uploadPartsUrlDetails.authorizationToken, DebugLevel.FullDebug); //Wait a while int secToWait = WebRequestAttempt * 2; Thread.Sleep(secToWait * 1000); //Increment counter WebRequestAttempt++; //Retry goto RetryPartUpload; } } //Close the upload part response uploadPartResponse.Close(); //Lock so we can work on uploadDetails lock (uploadDetailsLock) { //Update uploadDetails with the fact this part has been completed uploadDetails.BytesConfirmedSent = uploadDetails.BytesConfirmedSent + uploadDetailsSnapshot.bytesSentForPart; //Calculate the decimal amount completed double decimalPercentage = (double)uploadDetails.BytesConfirmedSent / (double)localFileSize; //Calculate the percentage completed int percentage = (int)(decimalPercentage * 100); //Output to the console the percentage completed StaticHelpers.UpdateSummary($"Progress: {percentage}%"); } //Log to the debugger what part we've just done StaticHelpers.DebugLogger("Uploaded Part " + uploadDetailsSnapshot.partNo, DebugLevel.Verbose); } #endregion //Check whether we have finished or if just this thread being killed: if (uploadDetails.totalBytesSent >= localFileSize) { //We have reached the end of parts to upload so tell the system not to upload anymore noMoreThreads = true; } StaticHelpers.DebugLogger("Internal upload worker is dead.", DebugLevel.Verbose); }