一般而言,企业服务里面的 nginx 全局设置的 timeout 时长都不会超过 15s,假设要上传一个 100M 的文件到服务端,平均上传速度 1M/s,那也得 100s 远远超过了 15s 的时长。
所以必须采用切片上传/断点续传的方式来实现前端小文件上传,后端合并成大文件。
前端切片
//slice upload
var file = document.getElementById('file').files[0];
var fileSize = file.size;
var chunkSize = 2 * 1024 * 1024; // buffer bytes
var offset = 0;
var filename = file.name;
while (offset < fileSize) {
var size = chunkSize + offset;
if (size > fileSize) {
size = fileSize;
}
//切片
var loaded = file.slice(offset, size, file.type);
offset = size;
var xmlRequest = new XMLHttpRequest();
xmlRequest.open('POST', config.contextPath + '/upload/api/file-slice', false);
var data = new FormData();
data.append('filename', filename);
data.append('type', file.type);
data.append('file', loaded);
xmlRequest.send(data);
}
切片上传中 file.slice(offset, size, file.type)
的 file.type 是保留切片之后的文件类型,可了解MIME 类型
后端接收
后端接收因每个服务框架不同而采用不同的处理,下面例子是 scalatra 框架的代码片段:
post("/api/file-slice") {
contentType = formats("json")
val fileName = params.getOrElse[String]("filename", "")
val out = new FileOutputStream(outputFileTmp(fileName), true)
fileParams.get("file") match {
case Some(file) => IOUtils.copy(file.getInputStream, out)
}
out.close()
Map(
"status" -> "ok"
)
}
我这边没有用 md5 的验证,而直接使用文件名来续写,这个按照每个人的不同需求做不同的处理,标准做法都应该给 md5 的验证,仅供参考。