2012-10-26 80 views
4

我想在C#中創建一個異步的文件上傳處理程序,並且可以通過AJAX異步請求提供文件進度更新。基本上,如果請求是POST,它會將一些信息加載到會話中,然後開始上傳,如果請求是GET,它會返回上傳的當前狀態(上傳的字節數,總字節數等)。我並不完全確定它需要是一個異步處理程序,但文件可能非常大,所以我認爲這最好。對於基礎異步處理程序,我使用了與此處理程序非常相似的東西,內容如下:MSDN article。我在下面發佈了我的代碼的一些關鍵部分。我遇到的問題是,在POST完成之前,我沒有收到任何GET信息。我會提到在這個例子中,我使用jQuery作爲GET請求,使用BlueImp來發布文件。ASP.Net異步HTTP文件上傳處理程序

的HTML和JavaScript

<input id="somefile" type="file" /> 

$(function() { 
    name = 'MyUniqueId130'; 
    var int = null; 
    $('#somefile').fileupload({ 
    url: '/fileupload.axd?key='+name, 
    done: function (e, data) { clearInterval(int); } 
    }); 

    $('#somefile').ajaxStart(function(){ 
    int = setInterval(function(){ 
    $.ajax({ 
     url: '/fileupload.axd?key='+name, 
     dataType: 'json', 
     async: true 
    }) 
    .done(function(e1, data1){ 
     if(!e1.InProgress || e1.Complete || e1.Canceled) 
     clearInterval(int); 
    }); 
    }, 10000)}); 
}); 

異步處理請求方法只是調用正確的方法是否是一個POST或GET來的那麼下面的調用CompleteRequest結束請求之一:

private static void GetFilesStatuses(HttpContext context) 
{ 
    string key = context.Request.QueryString["key"]; 
    //A dictionary of <string, UploadStatus> in the session 
    var Statuses = GetSessionStore(context); 
    UploadStatus ups; 

    if (!String.IsNullOrWhiteSpace(key)) 
    { 
    if (Statuses.TryGetValue(key, out ups)) 
    { 
     context.Response.StatusCode = (int)HttpStatusCode.OK; 
     context.Response.Write(CreateJson(ups)); 
    } 
    else 
    { 
     context.Response.StatusCode = (int)HttpStatusCode.NotFound; 
    } 
    } 
    else 
    { 
    context.Response.StatusCode = (int)HttpStatusCode.OK; 
    context.Response.Write(CreateJson(Statuses.Values)); 
    } 
} 

private static void UploadFile(HttpContext context) 
{ 
var Statuses = GetSessionStore(context); 
string key = context.Request.QueryString["key"]; 

if (String.IsNullOrWhiteSpace(key)) 
{ 
    context.Response.StatusCode = (int)HttpStatusCode.BadRequest; 
    return; 
} 

HttpPostedFile file = context.Request.Files[0]; 
string extn = file.FileName.LastIndexOf('.') == -1 ? "" : 
    file.FileName.Substring(file.FileName.LastIndexOf('.'), (file.FileName.Length - file.FileName.LastIndexOf('.'))); 
string temp = GetTempFileName(path, extn); 
UploadStatus status = new UploadStatus() 
{ 
    FileName = file.FileName, 
    TempFileName = temp, 
    Path = path, 
    Complete = false, 
    Canceled = false, 
    InProgress = false, 
    Success = true, 
    BytesLoaded = 0, 
    TotalBytes = file.ContentLength 
}; 
Statuses.Add(key, status); 
byte[] buffer = new byte[bufferSize]; 
int byteCount = 0; 

using (var fStream = System.IO.File.OpenWrite(context.Request.MapPath(path + temp))) 
{ 
    uploads.Add(status); 

    while ((byteCount = file.InputStream.Read(buffer, 0, bufferSize)) > 0 && !status.Canceled) 
    { 
    status.InProgress = true; 
    status.BytesLoaded += byteCount; 
    fStream.Write(buffer, 0, byteCount); 
    } 

    status.Complete = !status.Canceled; 
    status.InProgress = false; 
    status.Success = true; 

    if (status.Canceled) 
    { 
    Statuses.Remove(temp); 
    } 

    context.Response.StatusCode = (int)HttpStatusCode.OK; 
} 
} 

我已經嘗試了許多事情,例如非異步處理程序,異步處理程序,確保JavaScript運行異步,但此時我認爲我需要一些不同的眼睛來解決問題,所以感謝您提供任何人都可以提供的幫助。

+0

在遇到此問題的情況下,我創建了一個靜態ConcurrentDictionary,並在while循環內添加了一個TryUpdate來更新狀態。這固定的大部分問題,但我也改變了JavaScript使用BlueImps beforeSend函數來啓動異步請求 – ars265

回答

4

我假設你使用的是默認的ASP.Net會話管理器,我看到你打電話GetSessionStore來獲得你的會話。不幸的是,默認的會話管理器在一次調用需要對會話存儲進行寫入訪問時序列化所有請求。這StackOverflow question和這MSDN arcle on Session State有關於會話狀態和它的鎖定行爲一些非常有用的信息。

現在,爲了解決您的問題,您將不得不做一些事情,這取決於您是使用MVC控制器還是編寫自定義IHttpHandler。

  • 如果你正在寫自己的IHttpHandler,確保你有添加到您的處理程序IRequiresSessionStateIReadOnlySessionState接口。這樣做,管道將跳過尋找會話並直接處理。在這種情況下,context.Session將爲空。
  • 如果您使用MVC處理請求,則需要修改控制器類,SessionState attribute傳遞SessionStateBehavior,SessionStateBehavior.Disabled

無論哪種情況,您都無法依靠Session對象來存儲上載狀態。你可以創建一個靜態的ConcurrentDictionary,它們的SessionID被鍵入(你需要傳遞上傳查詢字符串或者自己讀取cookie,調用Session.SessionId會再次阻止你)並且將你的上傳狀態存儲在那裏看起來他們是Concurrent *)。

另一種選擇是用您自己的自定義提供程序替換SessionStateProvider,但在這種情況下可能會矯枉過正。

+0

我刪除了IRequiresSessionState並將GetSessionStore的內部替換爲剛剛返回對我的靜態ConncurentDictionary的引用(並更改了Add和Remove TryAdd和TryRemove),但我仍然遇到GetFilesStatuses獲取文件狀態的問題。上傳一個大文件現在花費的時間非常長,比以前更長,並且當它從GET請求返回時,對象引用似乎並沒有得到更新。 – ars265

+0

在這種情況下,調試器可以提供幫助。逐步通過這兩種方法(後,然後獲得),並確保事情正在設置你想要的方式。多線程可能很難習慣。刪除會話狀態不應該減慢你的上傳速度,所以我懷疑其他事情正在發生。此外,請確保您引用的是相同的字典,而不是每個請求中的新字典。 – Joshua