commons-fileupload框架源码解析(三)--ParseRequest

  1. commons-fileupload框架源码解析(一)--实例
  2. commons-fileupload框架源码解析(二)--HTTP
  3. commons-fileupload框架源码解析(三)--ParseRequest
  4. commons-fileupload框架源码解析(四)--FileItemIterator
  5. commons-fileupload框架源码解析(五)--MultipartStream
  6. commons-fileupload框架源码解析(六)--ParameterParser
  7. commons-fileupload框架源码解析(七)--FileCleaningTracker
  8. commons-fileupload框架源码解析(八)--DeferredFileOutputStream

第三章简介

现在,我们有了前两篇的基础,我们就开始解析源码,我们用第一篇的例子,尽量一行一行代码的讲解下去。我将第一篇的例子直接简称为实例。

源码解析

实例代码中

 DiskFileItemFactory factory = new DiskFileItemFactory();

DisFileItemFactory继承了FileItemFactory接口,并且并且重写了FileItemFactory的唯一方法public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName)该方法会对HTTP的主体内容进行封装,并提供了一系列配置方法,如内存存储数的阀值sizeThreshold,临时文件存储目录repository,还有默认的数据编码defaultCharset,还有非常重要的临时文件清理跟踪器。在例子中,我设置SizeTreshold为4096字节,就是4K,临时存储目录的路径是Tomcat里的实例项目文件下的temp文件下。

实例代码中

  ServletFileUpload upload = new ServletFileUpload(factory);

ServletFileUplad继承了FileUpload,FileUpload又继承了FileUpladBase抽象类,ServletFileUpload主要是将HttpServletRequest封装ServletRequestContext传给FileUpload处理,ServletRequestContext是个装饰者模式,不过装饰一下HttpServletRequest的getContentType方法,getContentLength方法,getCharacterEncoding方法,在1.4的版本中,我们还看到ServletRequestContext继承了UploadContext接口,该接口只有一个contentLength方法,ServletRequestContext重新该方法,用于获取HTTP消息头上Content-length的值,而官方认为HttpServletRequest.getContentLength()方法获取的值不标准,至少返回的类型是int的话,并不够用,所以新增了UploadContext接口来修复这个bug,返回long类型值。而这个UploadContext的接口,官方说明是会在2.0的时候去掉,并将contentLength重命名成新的方法名到RequestContext中。
而FileUpload只不过是重写FileUploadBase的获取和设置工厂FileItemFactory方法,将自定义的工厂FileItemFactory传进来,交给FileUploadBase使用。
FileUploadBase可以说这个整个框架的核心类之一,相当于这个框架业务的控制层。

upload.setSizeMax(1000000 * 20);

这个方法是对HTTP请求的最大字节数进行一个限制的方法。默认是-1,不限制最大字节数,我认为如果不是业务有特殊要求,一般还是要设置的,比较安全。

List fileItems = upload.parseRequest(req);

这个方法就是可以说就是我这系列博客的开端端和重点,我会从这里就行深入源码去解析,这个方法简单来说就是将请求主体的内容解析转换成更简单更容易操作的FileItem集合。
现在,我开始逐步说明其方法内容源码。

ServletFileUpload.parseRequest

 @Override
    public List parseRequest(HttpServletRequest request)
    throws FileUploadException {
        return parseRequest(new ServletRequestContext(request));
    }

之前已经说过,ServletFileUpload的职责,就是将HttpServletRequest封装成RequestContext,供给父类调用,所以具体的业务逻辑,要去父类上看,而ServletFileUpload的直属父类FileUpload,也只是重写一下父类的获取和设置工厂FileItemFactory的方法,也就是具体的业务还要往上看FileUploadBase

FileUploadBase

.parseRequest(RequestContext)

 public List parseRequest(RequestContext ctx)
            throws FileUploadException {
        List items = new ArrayList();
        boolean successful = false;
        try {
            FileItemIterator iter = getItemIterator(ctx);//将RequestContext的主体文本流的内容转换到FileItemIterator中
            FileItemFactory fac = getFileItemFactory();
            if (fac == null) {
                throw new NullPointerException("No FileItemFactory has been set.");
            }
            while (iter.hasNext()) {
                //将FileItemIterator的FileItemStream对象迭代出来,再封装成FileItemFactory所需要的FileItem.
                final FileItemStream item = iter.next();
                // Don't use getName() here to prevent an InvalidFileNameException.
                final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
                FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
                                                   item.isFormField(), fileName);
                items.add(fileItem);
                try {
                    //将参数内容的参数值数据复制到FileItem的流中。
                    Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
                } catch (FileUploadIOException e) {
                    throw (FileUploadException) e.getCause();
                } catch (IOException e) {
                    throw new IOFileUploadException(format("Processing of %s request failed. %s",
                                                           MULTIPART_FORM_DATA, e.getMessage()), e);
                }
                final FileItemHeaders fih = item.getHeaders();
                fileItem.setHeaders(fih);
            }
            successful = true;
            return items;
        } catch (FileUploadIOException e) {
            throw (FileUploadException) e.getCause();
        } catch (IOException e) {
            throw new FileUploadException(e.getMessage(), e);
        } finally {
            if (!successful) {
                for (FileItem fileItem : items) {
                    try {
                        fileItem.delete();
                    } catch (Exception ignored) {
                        // ignored TODO perhaps add to tracker delete failure list somehow?
                    }
                }
            }
        }
    }

从方法代码中可以看出,将RequestContext的主体文本流的参数内容封装业务,交给FileItemIterator中进行处理。而FileUploadBase.parseRequest(RequestContext)方法只做遍历迭代FileItemIterator,将存放在FileItemlterator的里面的FileItemStream拿出来,封装到FileItemFactory所提供FileItem,再将FileItem存放到集合中,返回出去,需要注意的是,在遍历FileItemIterator过程中,出现异常的情况下,所有的FileItem中的临时文件会立即删除。

你可能感兴趣的:(commons-fileupload框架源码解析(三)--ParseRequest)