问题
I have a simple IHttpHandler for chunked file transfer, and in the client code i'd like to report progress of percentage of the file, in the past I did it by dividing the amount of bytes transferred by the file size in bytes, and then make an event when it reached every 10% which I know is not the best method. I pulled out all that code regardless, but is there a better way to do it in the method I am using below?
//IHTTPMethod
//Open file
string file = System.IO.Path.GetFileName(pubAttFullPath.ToString());
FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
//Chunk size that will be sent to Server
int chunkSize = 49152;
// Unique file name
string fileName = Guid.NewGuid() + Path.GetExtension(file);
int totalChunks = (int)Math.Ceiling((double)fileStream.Length / chunkSize);
// Loop through the whole stream and send it chunk by chunk;
for (int i = 0; i < totalChunks; i++)
{
int startIndex = i * chunkSize;
int endIndex = (int)(startIndex + chunkSize > fileStream.Length ? fileStream.Length : startIndex + chunkSize);
int length = endIndex - startIndex;
byte[] bytes = new byte[length];
fileStream.Read(bytes, 0, bytes.Length);
//Request url, Method=post Length and data.
string requestURL = "http://localhost:16935/Transfer.ashx";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestURL);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
// Chunk(buffer) is converted to Base64 string that will be convert to Bytes on the handler.
string requestParameters = @"fileName=" + fileName + @"&secretKey=mySecret" +
"&data=" + HttpUtility.UrlEncode(Convert.ToBase64String(bytes));
// finally whole request will be converted to bytes that will be transferred to HttpHandler
byte[] byteData = Encoding.UTF8.GetBytes(requestParameters);
request.ContentLength = byteData.Length;
Stream writer = request.GetRequestStream();
writer.Write(byteData, 0, byteData.Length);
writer.Close();
// here we will receive the response from HttpHandler
StreamReader stIn = new StreamReader(request.GetResponse().GetResponseStream());
string strResponse = stIn.ReadToEnd();
stIn.Close();
}
My server end code is here: This is probably the best place to report the progress as i can update to SQL locally here.
public class IHTTPTransfer : IHttpHandler
{
/// <summary>
/// You will need to configure this handler in the web.config file of your
/// web and register it with IIS before being able to use it. For more information
/// see the following link: http://go.microsoft.com/?linkid=8101007
/// </summary>
#region IHttpHandler Members
public bool IsReusable
{
// Return false in case your Managed Handler cannot be reused for another request.
// Usually this would be false in case you have some state information preserved per request.
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
string accessCode = context.Request.Params["secretKey"].ToString();
if (accessCode == "mySecret")
{
string fileName = context.Request.Params["fileName"].ToString();
byte[] buffer = Convert.FromBase64String(context.Request.Form["data"]);
SaveFile(fileName, buffer);
}
}
public void SaveFile(string fileName, byte[] buffer)
{
string Path = @"C:\Filestorage\" + fileName;
FileStream writer = new FileStream(Path, File.Exists(Path) ? FileMode.Append : FileMode.Create, FileAccess.Write);
writer.Write(buffer, 0, buffer.Length);
writer.Close();
}
#endregion
}
回答1:
Once you have handler you can easily do progress. Below sample code:
/// <summary>
/// The download file.
/// </summary>
/// <param name="state">
/// The state.
/// </param>
/// <exception cref="InvalidDataException">
/// </exception>
private void DownloadFile()
{
string url = "http://localhost:16935/Transfer.ashx";
bool cancel = false;
string savePath = "C:/Temp";
try
{
// Put the object argument into an int variable
int start = 0;
if (File.Exists(savePath))
{
start = (int)new System.IO.FileInfo(savePath).Length;
}
// Create a request to the file we are downloading
var request = (HttpWebRequest)WebRequest.Create(url);
// Set the starting point of the request
request.AddRange(start);
// Set default authentication for retrieving the file
request.Credentials = CredentialCache.DefaultCredentials;
// Retrieve the response from the server
using (var response = (HttpWebResponse)request.GetResponse())
{
// check file lenght
int fileLength = 0;
int.TryParse(resp.Headers.Get("Content-Length"), out fileLength);
// Open the URL for download
using (Stream responseStream = response.GetResponseStream())
{
// Create a new file stream where we will be saving the data (local drive)
using (var fs = new FileStream(savePath, FileMode.Append, FileAccess.Write, FileShare.Read))
{
// It will store the current number of bytes we retrieved from the server
int bytesSize;
// A buffer for storing and writing the data retrieved from the server
var downBuffer = new byte[2048];
// Loop through the buffer until the buffer is empty
while ((bytesSize = responseStream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
var args = new CancelEventArgs(false);
this.OnCancel(args);
if (args.Cancel)
{
cancel = true;
break;
}
// Write the data from the buffer to the local hard drive
fs.Write(downBuffer, 0, bytesSize);
fs.Flush();
float percentComplete = 0;
if (fileLength > 0)
{
percentComplete = (100 * fs.Length) / (float)fileLength;
if (percentComplete > 100)
{
percentComplete = 100;
}
}
this.OnProgressChanged(new ProgressChangedEventArgs((int)percentComplete));
}
}
}
}
}
catch (Exception ex)
{
throw;
}
}
In code you have methods that fires events which you can subscribe:
/// <summary>
/// Adds or removes cancel event.
/// </summary>
public event CancelEventHandler Cancel;
/// <summary>
/// Adds or removes progress changed event.
/// </summary>
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
/// <summary>
/// Raises progress changed event.
/// </summary>
/// <param name="e">
/// <see cref="ProgressChangedEventArgs"/> params.
/// </param>
protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
EventHandler<ProgressChangedEventArgs> handler = this.ProgressChanged;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Raises cancel event.
/// </summary>
/// <param name="e">
/// </param>
protected virtual void OnCancel(CancelEventArgs e)
{
CancelEventHandler handler = this.Cancel;
if (handler != null)
{
handler(this, e);
}
}
来源:https://stackoverflow.com/questions/10471127/ihttphandler-file-transfer-progress