我正在尝试在 C# 中创建一个异步的文件上传处理程序,并且可以通过 AJAX 异步请求提供有关文件进度的更新。基本上,如果请求是 POST,它会将一些信息加载到 session 中,然后开始上传,如果请求是 GET,它会返回上传的当前状态(上传的字节数、总字节数等)。我不完全确定它需要是一个异步处理程序,但文件可能非常大,所以我认为这样效果最好。对于基本异步处理程序,我使用了与此 MSDN article 中的处理程序非常相似的东西。 .我在下面发布了我的代码的一些关键部分。我遇到的问题是在 POST 完成之前我没有收到任何 GET 信息。我会提到在这个例子中我使用的是 jQuery对于 GET 请求和 BlueImp用于发布文件。
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 异步运行,但在这一点上我认为我需要对这个问题有不同的看法,所以感谢您提供的任何帮助.
最佳答案
我假设您使用的是默认的 ASP.Net session 管理器,并且我看到您调用了 GetSessionStore
来获取您的 session 。不幸的是,当调用需要对 session 存储的写访问权限时,默认 session 管理器会序列化所有请求。这StackOverflow question还有这个MSDN arcle on Session State有一些关于 session 状态及其锁定行为的非常有用的信息。
现在,为了解决您的问题,您将不得不做一些事情,这取决于您是使用 MVC Controller 还是编写自定义 IHttpHandler。
- 如果您正在编写自己的 IHttpHandler,请确保您没有将
IRequiresSessionState
或IReadOnlySessionState
接口(interface)添加到您的处理程序。这样做时,管道将跳过寻找 session 并直接进行处理。context.Session
在这种情况下将为空。 - 如果您使用 MVC 来处理请求,则需要使用 SessionState attribute 装饰您的 Controller 类传入 SessionStateBehavior
SessionStateBehavior.Disabled
的。
在任何一种情况下,您都无法依赖 Session 对象来存储您的上传状态。您可以创建一个静态的 ConcurrentDictionary 从他们的 SessionID 键控(您需要传入上传查询字符串或自己读取 cookie,调用 Session.SessionId 只会再次阻止您)并将您的上传状态存储在那里(这看起来它们也是并发的*)。
另一种选择是将 SessionStateProvider 替换为您自己的自定义提供程序,但在这种情况下这可能有点矫枉过正。
关于c# - ASP.Net 异步 HTTP 文件上传处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13088753/