2012-10-26 2 views
4

비동기식이며 AJAX 비동기 요청을 통해 파일 진행 상황에 대한 업데이트를 제공 할 수있는 C# 파일 업로드 처리기를 만들려고합니다. 기본적으로 요청이 POST 인 경우 세션에 정보를로드 한 다음 요청이 GET 인 경우 업로드의 현재 상태 (업로드 된 바이트, 총 바이트 등)를 반환합니다. 나는 그것이 비동기 핸들러가 될 필요가 있다는 것을 완전히 확신하지는 않지만, 파일이 꽤 클 수 있기 때문에 가장 잘 작동 할 것이라고 생각했다. 기본 비동기 처리기의 경우 처리기와 매우 비슷한 내용을이 MSDN article에 사용했습니다. 아래 코드의 일부 핵심 섹션을 게시했습니다. 내가 가지고있는 문제는 POST가 완료 될 때까지 GET 정보를받지 못한다는 것입니다. 이 예에서는 GET 요청에 jQuery을 사용하고 파일을 게시하는 데는 BlueImp을 사용합니다.ASP.Net 비동기 HTTP 파일 업로드 처리기

HTML과 자바 스크립트는 비동기 프로세스 요청 방법은

<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가인지 정확한 메소드를 호출하거나 다음 요청 종료 CompleteRequest를 호출하여 다음 중 하나에 GET

:

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를 추가하여 상태를 업데이트했습니다.이것은 대부분의 문제를 해결했지만 BlueImps beforeSend 함수를 사용하여 비동기 요청을 시작하도록 자바 스크립트를 변경했습니다. – ars265

답변

4

기본 ASP.Net 세션 관리자를 사용하고 있다고 가정하고 세션을 얻으려면 GetSessionStore으로 전화하는 것이 좋습니다. 유감스럽게도 기본 세션 관리자는 호출에 세션 저장소에 대한 쓰기 액세스가 필요할 때 모든 요청을 직렬화합니다. 이 StackOverflow question과이 MSDN arcle on Session State에는 세션 상태에 대한 유용한 정보가 있으며 잠금 동작이 있습니다.

이제 문제를 처리하기 위해 MVC 컨트롤러를 사용하는지 아니면 사용자 지정 IHttpHandler를 작성하는지에 따라 몇 가지 작업을 수행해야합니다. 당신이 당신의 자신을 IHttpHandler를 작성하는 경우

  • , 당신은 하지이 핸들러에 추가 IRequiresSessionState 또는 IReadOnlySessionState 인터페이스가 않습니다 있는지 확인하십시오. 이렇게하면 파이프 라인은 세션을 찾지 않고 바로 처리됩니다. 이 상황에서는 context.Session이 null입니다.
  • MVC를 사용하여 요청을 처리하는 경우 SessionStateBehaviorSessionState attribute 인 컨트롤러 클래스를 SessionStateBehavior.Disabled으로 꾸며야합니다.

두 경우 모두 세션 상태에 따라 업로드 상태를 저장할 수 없습니다. 정적 인 ConcurrentDictionary를 SessionID 키를 사용하여 만들 수 있습니다 (업로드 쿼리 문자열을 전달하거나 쿠키를 직접 읽어야하며 Session.SessionId를 호출하면 다시 차단할 수 있음). 그리고 업로드 상태를 저장합니다. 그들은 동시대 인 것처럼 보입니다.)

또 다른 옵션은 사용자 지정 공급자로 SessionStateProvider를 바꾸는 것이지만이 상황에서는 과도 할 수 있습니다.

+0

IRequiresSessionState를 제거하고 GetSessionStore의 내부를 바꿔서 내 정적 ConncurentDictionary에 대한 참조를 반환했습니다. TryAdd 및 TryRemove)하지만 여전히 파일 상태를 가져 오는 GetFilesStatuses에 문제가 있습니다. 대용량 파일을 업로드하는 데 시간이 오래 걸리며 이전보다 훨씬 길어졌으며 개체 참조가 GET 요청에서 돌아 왔을 때 업데이트되지 않는 것처럼 보입니다. – ars265

+0

이 경우 디버거가 도움이 될 수 있습니다. 두 가지 방법 (게시물, get)을 단계별로 수행하고 원하는 방식대로 설정되었는지 확인하십시오. 멀티 스레딩은 익숙하지 않을 수 있습니다. 세션 상태를 제거해도 업로드 속도가 느려져서는 안되며 다른 일이 벌어지고있는 것 같습니다. 또한 각 요청에 대해 새 사전이 아니라 동일한 사전을 참조하는지 확인하십시오. – Joshua