using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Media;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using MTOM_Library;
using System.Web.UI;
namespace UploadWinClient
{
public partial class Form1 : Form
{
public static MTOM_Library.MtomWebService.MTOMWse WebService;
#region constructor, load, exit methods
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
WebService = new MTOM_Library.MtomWebService.MTOMWse();
// get the list of files to download from the server
workerGetFileList.RunWorkerAsync();
// set the default save folder
this.txtSaveFolder.Text = Application.StartupPath;
// configure the 'TaskPanel' which is used to dynamically show a progress bar + status message for each file transfer operation
this.taskPanel1.RemoveItemsWhenFinished = true;
this.taskPanel1.RemoveItemsOnError = false;
this.taskPanel1.AutoSizeForm = false;
// init the ThreadPool MaxThread size to the value in the control
this.dudMaxThreads_ValueChanged(sender, e);
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void Ding()
{
try
{
SoundPlayer sp = new SoundPlayer();
sp.Stream = this.GetType().Assembly.GetManifestResourceStream("UploadWinClient.download_complete.wav");
sp.Play();
}
catch { } // ignore errors playing sound file
}
#endregion
#region upload code
private void btnBrowse_Click(object sender, EventArgs e)
{
this.openFileDialog1.Title = "Select file(s) to upload";
if(this.openFileDialog1.ShowDialog() == DialogResult.OK)
{
if(this.openFileDialog1.FileNames.Length == 0)
return; // can't upload unless a file is selected
if(this.chkLoginRequired.Checked && !AuthenticateWebService())
return;
foreach(string path in this.openFileDialog1.FileNames)
{
string guid = Guid.NewGuid().ToString();
this.taskPanel1.AddOperation(new TaskPanelOperation(guid, String.Format("Uploading {0}", Path.GetFileName(path)), ProgressBarStyle.Blocks));
ThreadPool.QueueUserWorkItem(new WaitCallback(this.UploadFile), new Triplet(guid, path, 0));
}
}
}
/// <summary>
/// This method is used as a thread start function.
/// The parameter is a Triplet because the ThreadStart can only take one object parameter
/// </summary>
/// <param name="triplet">First=guid; Second=path; Third=offset</param>
private void UploadFile(object triplet)
{
string guid = (triplet as Triplet).First.ToString();
string path = (triplet as Triplet).Second.ToString();
long offset = Int64.Parse((triplet as Triplet).Third.ToString());
FileTransferUpload ftu = new FileTransferUpload();
ftu.WebService.CookieContainer = WebService.CookieContainer; // copy the CookieContainer into the transfer object (for auth cookie, if relevant)
ftu.Guid = guid;
// set up the chunking options
if(this.chkAutoChunksize.Checked)
ftu.AutoSetChunkSize = true;
else
{
ftu.AutoSetChunkSize = false;
ftu.ChunkSize = (int)this.dudChunkSize.Value * 1024; // kb
}
// set the remote file name and start the background worker
ftu.LocalFilePath = path;
ftu.IncludeHashVerification = this.chkHash.Checked;
ftu.ProgressChanged += new ProgressChangedEventHandler(ft_ProgressChanged);
ftu.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ft_RunWorkerCompleted);
ftu.RunWorkerSync(new DoWorkEventArgs(offset));
}
private void chkUploadAutoChunksize_CheckedChanged(object sender, EventArgs e)
{
this.dudChunkSize.Enabled = !this.chkAutoChunksize.Checked;
}
#endregion
#region download code
/// <summary>
/// The same button is used to start the download, and cancel it. (the text changes as needed)
/// Note: the upload and download operations have separate background workers.
/// </summary>
private void btnDownload_Click(object sender, EventArgs e)
{
if(this.lstDownloadFiles.SelectedItems.Count == 0)
return; // can't download unless a file is selected
if(this.chkLoginRequired.Checked && !AuthenticateWebService())
return;
List<string> files = new List<string>(this.lstDownloadFiles.SelectedItems.Count);
foreach(ListViewItem item in this.lstDownloadFiles.SelectedItems)
{
string guid = Guid.NewGuid().ToString();
this.taskPanel1.AddOperation(new TaskPanelOperation(guid, String.Format("Downloading {0}", Path.GetFileName(item.Text)), ProgressBarStyle.Blocks));
ThreadPool.QueueUserWorkItem(new WaitCallback(this.DownloadFile), new Triplet(guid, item.Text, 0));
}
}
/// <summary>
/// This method is used as a thread start function.
/// The parameter is a Triplet because the ThreadStart can only take one object parameter
/// </summary>
/// <param name="triplet">First=guid; Second=path; Third=offset</param>
private void DownloadFile(object triplet)
{
string guid = (triplet as Triplet).First.ToString();
string path = (triplet as Triplet).Second.ToString();
long offset = Int64.Parse((triplet as Triplet).Third.ToString());
FileTransferDownload ftd = new FileTransferDownload();
ftd.WebService.CookieContainer = WebService.CookieContainer; // copy the CookieContainer into the transfer object (for auth cookie, if relevant)
ftd.Guid = guid;
// set up the chunking options
if(this.chkAutoChunksize.Checked)
ftd.AutoSetChunkSize = true;
else
{
ftd.AutoSetChunkSize = false;
ftd.ChunkSize = (int)this.dudChunkSize.Value * 1024; // kb
}
// set the remote file name and start the background worker
ftd.RemoteFileName = Path.GetFileName(path);
if(String.IsNullOrEmpty(this.txtSaveFolder.Text.Trim()))
ftd.LocalSaveFolder = Application.StartupPath; // by default
else
ftd.LocalSaveFolder = this.txtSaveFolder.Text;
ftd.IncludeHashVerification = this.chkHash.Checked;
ftd.ProgressChanged += new ProgressChangedEventHandler(ft_ProgressChanged);
ftd.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ft_RunWorkerCompleted);
ftd.RunWorkerSync(new DoWorkEventArgs(0));
}
void ft_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
string guid = (sender as FileTransferBase).Guid;
if(e.Error != null)
{
this.taskPanel1.EndOperation(guid, e.Error);
return;
}
else if(e.Cancelled)
{
this.taskPanel1.EndOperation(guid, new Exception("Cancelled"));
return;
}
else
{
this.taskPanel1.EndOperation(guid, null);
if(this.taskPanel1.List.Count == 0) // play a ding on last item
Ding();
}
}
void ft_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.taskPanel1.ProgressChanged((sender as FileTransferBase).Guid, e.ProgressPercentage, e.UserState.ToString());
}
#endregion
#region Get Server File List code
private void btnRefreshFiles_Click(object sender, EventArgs e)
{
if(this.workerGetFileList.IsBusy)
{
this.workerGetFileList.CancelAsync();
Thread.Sleep(1000);
if(this.workerGetFileList.IsBusy)
{
MessageBox.Show("Worker thread is busy and did not respond to a cancel, try again in a few seconds.", "Busy");
return;
}
}
// refresh the list of files from the Upload folder on the server.
this.workerGetFileList.RunWorkerAsync();
}
/// <summary>
/// This method fetches a string[] of filenames from the Upload folder on the server.
/// </summary>
private void workerGetFileList_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
if(e.Error.Message.Contains("ReturnUrl=") || e.Error.Message.Contains("forcibly closed"))
{
this.lblErrors.Text = "Forms Authentication Required";
this.chkLoginRequired.Checked = true;
this.txtUsername.Focus();
return;
}
else
this.lblErrors.Text = "Could not list files on server: " + e.Error.Message;
return;
}
this.lblErrors.Text = "";
this.lstDownloadFiles.Items.Clear();
// copy the list of filenames into the listbox
foreach(string s in e.Result as string[])
this.lstDownloadFiles.Items.Add(s);
}
private void workerGetFileList_DoWork(object sender, DoWorkEventArgs e)
{
// fetch the list of filenames from the web service
string[] files = WebService.GetFilesList();
// set the result with the return value (available to workerGetFileList_RunWorkerCompleted method as e.Result)
e.Result = files;
}
#endregion
#region manual resume code, manual MD5 hash
private void manualResumeDownloadToolStripMenuItem_Click(object sender, EventArgs e)
{
this.openFileDialog1.Title = "Select partially complete download file";
if(this.openFileDialog1.ShowDialog() == DialogResult.Cancel)
return;
long offset = new FileInfo(this.openFileDialog1.FileName).Length;
this.txtSaveFolder.Text = Path.GetDirectoryName(this.openFileDialog1.FileName);
string guid = Guid.NewGuid().ToString();
this.taskPanel1.AddOperation(new TaskPanelOperation(guid, String.Format("Downloading {0}", Path.GetFileName(this.openFileDialog1.FileName)), ProgressBarStyle.Blocks));
ThreadPool.QueueUserWorkItem(new WaitCallback(this.DownloadFile), new Triplet(guid, this.openFileDialog1.FileName, offset));
}
private void manualResumeUploadToolStripMenuItem_Click(object sender, EventArgs e)
{
this.openFileDialog1.Title = "Select the original local file to upload";
if(this.openFileDialog1.ShowDialog() == DialogResult.Cancel)
return;
// get the offset from the server (which has the partial file)
long offset = WebService.GetFileSize(Path.GetFileName(this.openFileDialog1.FileName));
string guid = Guid.NewGuid().ToString();
this.taskPanel1.AddOperation(new TaskPanelOperation(guid, String.Format("Uploading {0}", Path.GetFileName(this.openFileDialog1.FileName)), ProgressBarStyle.Blocks));
ThreadPool.QueueUserWorkItem(new WaitCallback(this.UploadFile), new Triplet(guid, this.openFileDialog1.FileName, offset));
}
private void computeMD5HashToolStripMenuItem_Click(object sender, EventArgs e)
{
this.openFileDialog1.Title = "Select local file to hash";
if(this.openFileDialog1.ShowDialog() == DialogResult.Cancel)
return;
this.lblStatusBarText.Text = "Calculating file hash...";
this.Cursor = Cursors.WaitCursor;
this.workerFileHash.RunWorkerAsync(this.openFileDialog1.FileName);
}
private void workerFileHash_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = FileTransferBase.CalcFileHash(e.Argument.ToString());
}
private void workerFileHash_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.lblErrors.Text = "[Hash copied to clipboard]: " + e.Result.ToString();
Clipboard.SetData(DataFormats.StringFormat, e.Result.ToString());
this.lblStatusBarText.Text = "Ready";
this.Cursor = Cursors.Default;
}
#endregion
#region forms auth code
private void chkLoginRequired_CheckedChanged(object sender, EventArgs e)
{
// enable or disable the username/password boxes
this.lblUsername.Enabled = this.lblPassword.Enabled = this.txtUsername.Enabled = this.txtPassword.Enabled = this.chkLoginRequired.Checked;
}
private void btnLogin_Click(object sender, EventArgs e)
{
try
{
if(AuthenticateWebService())
{
this.lblErrors.Text = "";
this.lblStatusBarText.Text = "Login OK";
this.btnRefreshFiles.PerformClick();
}
else
this.lblErrors.Text = "Login failed";
}
catch(Exception ex)
{
this.lblErrors.Text = "Login failed: " + ex.Message;
}
}
private bool AuthenticateWebService()
{
if(WebService.CookieContainer != null && WebService.CookieContainer.Count > 0)
return true; // already authenticated.
// send a HTTP web request to the login.aspx page, using the querystring to pass in username and password
string postData = String.Format("?Username={0}&Password={1}", this.txtUsername.Text, this.txtPassword.Text);
string url = WebService.Url.Replace("MTOM.asmx", "") + "Login.aspx" + postData; // get the path of the login page, assuming it is in the same folder as the web service
HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;
req.CookieContainer = new CookieContainer();
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
// copy the cookie container to the web servicreqes
WebService.CookieContainer = req.CookieContainer;
return (response.Cookies.Count > 0); // true if the server sent an auth cookie, i.e. authenticated successfully
}
#endregion
private void dudMaxThreads_ValueChanged(object sender, EventArgs e)
{
ThreadPool.SetMaxThreads((int)this.dudMaxThreads.Value, (int)this.dudMaxThreads.Value);
}
private void chkAutoChunkSize_CheckedChanged(object sender, EventArgs e)
{
this.dudChunkSize.Enabled = !this.chkAutoChunksize.Checked;
}
private void lstDownloadFiles_SelectedIndexChanged(object sender, EventArgs e)
{
int numItems = this.lstDownloadFiles.SelectedItems.Count;
this.btnDownload.Enabled = numItems > 0;
if(numItems == 1)
this.lblFileSizeDownload.Text = String.Format("File size: {0}", FileTransferBase.CalcFileSize(WebService.GetFileSize(this.lstDownloadFiles.SelectedItems[0].Text)));
else
this.lblFileSizeDownload.Text = "";
}
private void btnBrowseSaveFolder_Click(object sender, EventArgs e)
{
if(this.folderBrowserDialog1.ShowDialog() != DialogResult.Cancel)
this.txtSaveFolder.Text = this.folderBrowserDialog1.SelectedPath;
}
}
}