org.nutz.mvc.upload.FastUploading Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nutz Show documentation
Show all versions of nutz Show documentation
Nutz, which is a collections of lightweight frameworks, each of them can be used independently
package org.nutz.mvc.upload;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import org.nutz.filepool.FilePool;
import org.nutz.http.Http;
import org.nutz.lang.Lang;
import org.nutz.lang.Streams;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.Mvcs;
import org.nutz.mvc.upload.util.BufferRing;
import org.nutz.mvc.upload.util.MarkMode;
import org.nutz.mvc.upload.util.RemountBytes;
/**
* 采用成块写入的方式,这个逻辑比 SimpleUploading 大约快了 1 倍
*
* @author zozoh([email protected])
*/
public class FastUploading implements Uploading {
private static final Log log = Logs.get();
public Map parse(HttpServletRequest req, UploadingContext context)
throws UploadException {
if (log.isDebugEnabled())
log.debug("FastUpload : " + Mvcs.getRequestPath(req));
/*
* 初始化一些临时变量
*/
int bufferSize = context.getBufferSize();
String charset = context.getCharset();
FilePool tmps = context.getFilePool();
int maxFileSize = context.getMaxFileSize();
/*
* 创建进度对象
*/
UploadInfo info = Uploads.createInfo(req);
if (log.isDebugEnabled())
log.debug("info created");
/*
* 创建参数表
*/
NutMap params = Uploads.createParamsMap(req);
if (log.isDebugEnabled())
log.debugf("Params map created - %s params", params.size());
/*
* 解析边界
*/
String firstBoundary = "--" + Http.multipart.getBoundary(req.getContentType());
RemountBytes firstBoundaryBytes = RemountBytes.create(firstBoundary);
String itemEndl = "\r\n--" + Http.multipart.getBoundary(req.getContentType());
RemountBytes itemEndlBytes = RemountBytes.create(itemEndl);
RemountBytes nameEndlBytes = RemountBytes.create("\r\n\r\n");
if (Http.multipart.getBoundary(req.getContentType()) == null) {
if (log.isInfoEnabled())
log.info("boundary no found!!");
return params;
}
if (log.isDebugEnabled())
log.debug("boundary: " + itemEndl);
/*
* 准备缓冲环,并跳过开始标记
*/
MarkMode mm;
BufferRing br;
try {
ServletInputStream ins = req.getInputStream();
// 构建 3 个环节点的缓冲环
br = new BufferRing(ins, 3, bufferSize);
// 初始加载
info.current = br.load();
// 跳过开始的标记
mm = br.mark(firstBoundaryBytes);
// 这是不可能的,应该立即退出
if (mm != MarkMode.FOUND) {
if (log.isWarnEnabled())
log.warnf("Fail to find the firstBoundary (%s) in stream, quit!", firstBoundary);
return params;
}
br.skipMark();
if (log.isDebugEnabled())
log.debug("skip first boundary");
}
catch (IOException e) {
throw Lang.wrapThrow(e);
}
/**
* ========================================================
* 进入循环
*/
if (log.isDebugEnabled())
log.debug("Reading...");
try {
FieldMeta meta;
do {
info.current = br.load();
// 标记项目头
mm = br.mark(nameEndlBytes);
String s = br.dumpAsString(charset);
// 肯定碰到了 "--\r\n", 这标志着整个流结束了
if ("--".equals(s) || MarkMode.STREAM_END == mm) {
break;
}
// 找到头的结束标志
else if (MarkMode.FOUND == mm) {
meta = new FieldMeta(s);
}
// 这是不可能的,抛错
else {
throw new UploadInvalidFormatException("Fail to found nameEnd!");
}
if(log.isDebugEnabled())
log.debugf("Upload File info: FilePath=[%s],fieldName=[%s]",meta.getFileLocalPath(),meta.getName());
// 作为文件读取
if (meta.isFile()) {
if (log.isDebugEnabled())
log.debugf("Upload Info: name=%s,content_type=%s", meta.getFileLocalName(),meta.getContentType());
// 检查是否通过文件名过滤
if (!context.isNameAccepted(meta.getFileLocalName())) {
throw new UploadUnsupportedFileNameException(meta);
}
// 检查是否通过文件类型过滤
if (!context.isContentTypeAccepted(meta.getContentType())) {
throw new UploadUnsupportedFileTypeException(meta);
}
// 上传的是一个空文件
if ("\"\"".equals(meta.getName()) || Strings.isBlank(meta.getFileLocalPath())) {
do {
info.current = br.load();
mm = br.mark(itemEndlBytes);
assertStreamNotEnd(mm);
br.skipMark();
} while (mm == MarkMode.NOT_FOUND);
}
// 保存临时文件
else {
File tmp = tmps.createFile(meta.getFileExtension());
OutputStream ops = null;
try {
ops = new BufferedOutputStream( new FileOutputStream(tmp),
bufferSize * 2);
// 需要限制文件大小
if (maxFileSize > 0) {
long maxPos = info.current + maxFileSize;
do {
info.current = br.load();
mm = br.mark(itemEndlBytes);
assertStreamNotEnd(mm);
if (info.current > maxPos) {
throw new UploadOutOfSizeException(meta);
}
br.dump(ops);
if(info.stop)
throw new UploadStopException(info);
} while (mm == MarkMode.NOT_FOUND);
}
// 不限制文件大小
else {
do {
info.current = br.load();
mm = br.mark(itemEndlBytes);
assertStreamNotEnd(mm);
br.dump(ops);
if(info.stop)
throw new UploadStopException(info);
} while (mm == MarkMode.NOT_FOUND);
}
}
finally {
Streams.safeFlush(ops);
Streams.safeClose(ops);
}
// 如果是空文件,不保存
if (context.isIgnoreNull() && tmp.length() == 0) {}
// 默认,空文件也保存
else {
params.add(meta.getName(), new TempFile(meta, tmp));
}
}
}
// 作为提交值读取
else {
StringBuilder sb = new StringBuilder();
do {
info.current = br.load();
mm = br.mark(itemEndlBytes);
assertStreamNotEnd(mm);
sb.append(br.dumpAsString(charset));
} while (mm == MarkMode.NOT_FOUND);
params.add(meta.getName(), sb.toString());
if (log.isDebugEnabled())
log.debugf( "Found a param, name=[%s] value=[%s]",
meta.getName(),
sb.toString());
}
} while (mm != MarkMode.STREAM_END);
}
// 处理异常
catch (IOException e) {
throw Lang.wrapThrow(e, UploadException.class);
}
// 安全关闭输入流
finally {
br.close();
}
if (log.isDebugEnabled())
log.debugf("...Done %s bytes readed", br.readed());
/**
* 全部结束
* ========================================================
*/
return params;
}
private static void assertStreamNotEnd(MarkMode mm) throws UploadInvalidFormatException {
if (mm == MarkMode.STREAM_END)
throw new UploadInvalidFormatException("Should not end stream");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy