最近公司做的项目有个需求是需要支持一次性上传几十个文件,单个文件最大支持几十M,而且还需要和另一项目组的项目配合,也就是先在A项目保存,再调用B项目接口做上传。为了提高上传速度用了多线程,用多线程和常规上传做了一下简单对比。
前端代码
<form enctype="multipart/form-data">
<input type="file" id="files" multiple /><br />
<button type="button" id="btnUpload">上传</button>
</form>
<div id="res"></div>
<script>
$('#btnUpload').click(function () {
var formData = new FormData();
var files = document.getElementById('files').files;
console.log(files[0]);
for (var i = 0; i < files.length; i++) {
formData.append("file"+i, files[0]);
}
$.ajax({
url: '/home/UploadMultiFilesAsync',
type: 'post',
data: formData,
async: false,
cache: false,
contentType: false, //不设置内容类型
processData: false, //不处理数据
success: function (response) {
var html = $('#res').html();
$('#res').html(html +"<br/>"+ response);
}
});
});
</script>
常规方式:
public ActionResult UploadMultiFiles()
{
var time = new System.Diagnostics.Stopwatch();
time.Start();
IDbConnection t = null;
var files = Request.Files;
string baseDir = AppDomain.CurrentDomain.BaseDirectory + "UploadFiles/" + DateTime.Now.Year+"/"+DateTime.Now.Month;
string sql = "insert into T_File(fileName,filePath) values(@fileName,@filePath);select scope_identity();";
if (!Directory.Exists(baseDir))
{
Directory.CreateDirectory(baseDir);
}
List<FileModel> models = new List<FileModel>(files.Count);
for(var i=0;i<files.Count;i++)
{
try
{
var file = files[i];
string fileName = Guid.NewGuid().ToString() + file.FileName;
string filePath = baseDir + "\\" + fileName;
file.SaveAs(filePath);
models.Add(new FileModel()
{
filePath = filePath,
fileName = fileName
});
}
catch(Exception e)
{
}
}
if (models.Count > 0)
{
IDbTransaction trans = null;
try
{
trans = db.DbConnection.BeginTransaction();
foreach (var item in models)
{
item.id=db.DbConnection.QueryFirstOrDefault<long>(sql, new { fileName = item.fileName, filePath = item.filePath }, trans);
}
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
}
}
time.Stop();
var span = time.Elapsed;
return Json(models.Count+"个文件,总时间(ms):"+span.TotalMilliseconds, JsonRequestBehavior.AllowGet);
}
测试结果:
平均在5s左右。
多线程方式:
public ActionResult UploadMultiFilesAsync()
{
var time = new System.Diagnostics.Stopwatch();
time.Start();
var files = Request.Files;
string baseDir = AppDomain.CurrentDomain.BaseDirectory + "UploadFiles\\" + DateTime.Now.Year + "\\" + DateTime.Now.Month;
string sql = "insert into T_File(fileName,filePath) values(@fileName,@filePath);select scope_identity();";
if (!Directory.Exists(baseDir))
{
Directory.CreateDirectory(baseDir);
}
List<FileModel> models = new List<FileModel>(files.Count);
Task parent = new Task(() => {
var ctx = new CancellationTokenSource();
var tf = new TaskFactory<int>(ctx.Token,
TaskCreationOptions.AttachedToParent,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default
);
var childTasks = new List<Task<int>>(files.Count);
for (var i = 0; i < files.Count; i++)
{
var file = files[i];
string fileName = Guid.NewGuid().ToString() + file.FileName;
string filePath = baseDir + "\\" + fileName;
models.Add(new FileModel()
{
filePath = filePath,
fileName = fileName
});
childTasks.Add(tf.StartNew(() => {
file.SaveAs(filePath);
if (i == 1 || i == 3 || i == 5)
throw new Exception("出错了");
return i;
},ctx.Token));
}
//任何子任务抛出异常,就remove item
for (int task = 0; task < childTasks.Count; task++)
{
childTasks[task].ContinueWith(t => models.Remove(models[task]), TaskContinuationOptions.OnlyOnFaulted);
}
});
double span=0;
//子任务完成后就保存到数据库
parent.ContinueWith(p => {
if (models.Count > 0)
{
IDbTransaction trans = null;
try
{
trans = db.DbConnection.BeginTransaction();
foreach (var item in models)
{
item.id = db.DbConnection.QueryFirstOrDefault<long>(sql, new { fileName = item.fileName, filePath = item.filePath }, trans);
}
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
}
finally
{
db.DbConnection.Close();
db.DbConnection.Dispose();
}
}
});
parent.Start();
parent.Wait();
time.Stop();
span = time.Elapsed.TotalMilliseconds;
return Json(models.Count + "个文件,总时间(ms):" + span, JsonRequestBehavior.AllowGet);
}
测试结果:
平均在3s左右,所以可以看到多线程上传比常规上传快了40%左右,当然这里只是做了简单测试,具体效果还有待更精确的测试。