Package org.nutz.mvc.upload

Source Code of org.nutz.mvc.upload.FastUploading

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(zozohtnt@gmail.com)
*/
public class FastUploading implements Uploading {

  private static final Log log = Logs.get();

  public Map<String, Object> 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);
    }

    /**
     * ========================================================<br>
     * 进入循环
     */
    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 BufferedOutputStreamnew 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());
    /**
     * 全部结束<br>
     * ========================================================
     */

    return params;
  }

  private static void assertStreamNotEnd(MarkMode mm) throws UploadInvalidFormatException {
    if (mm == MarkMode.STREAM_END)
      throw new UploadInvalidFormatException("Should not end stream");
  }
}
TOP

Related Classes of org.nutz.mvc.upload.FastUploading

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.