源码(六) - DiskFileItem

一.概述

1. FileItem表示在multipart / form-data POST请求中接收到的文件或表单项
2. DiskFileItem是FileItem接口的实现类

二.DiskFileItemFactory

 org.apache.commons.fileupload.FileItemFactory类用于创建org.apache.commons.fileupload.FileItem的实例fileItem
  1.  当fileItem对应的内容较小,将其保留在内存中
  2.  当fileItem对应的内容较大时,将其缓存到本地磁盘的一个临时文件中
 到底是保留在内存还是缓存到临时文件,由阈值大小threshold决定
  1. 内容字节数大于threshold,缓存到临时文件,临时文件自己配置
  2. 内容字节数小于threshold,保留在内存中
 
 如果没有配置阈值和临时文件目录,使用默认值:
  1.  阈值大小为10KB
  2.  临时文件目录获取:System.getProperty("java.io.tmpdir")
 当使用DiskFileItemFactory,需要考虑:
 临时文件在不再需要时立即被自动删除(更确切地说,当{@link java.io.File}的对应实例被垃圾回收时)。
 清理这些文件是由{@link FileCleaningTracker}的一个实例和相关的线程完成的。 
 在复杂的环境中,例如在Web应用程序中,您应该考虑终止此线程
 查看: http://commons.apache.org/proper/commons-fileupload/using.html     Java 文件上传组件 Apache Commons FileUpload 应用指南(四)清除资源

三.DiskFileItem

 接口org.apache.commons.fileupload.FileItem FileItem的默认实现
 从org.apache.commons.fileupload.DiskFileUpload实例检索此类的实例后,
  1.  可以使用#get()一次性请求文件的所有内容,
  2.  也可以使用#getInputStream()获取java.io.InputStream InputStream并处理该文件,而不尝试将其加载到内存中,这可能会使用大型文件。

四.源码

package org.apache.commons.fileupload.disk;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemHeaders;
import org.apache.commons.fileupload.FileItemHeadersSupport;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ParameterParser;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.DeferredFileOutputStream;

/**
 * 接口org.apache.commons.fileupload.FileItem FileItem的默认实现
 * 从org.apache.commons.fileupload.DiskFileUpload实例检索此类的实例后,
 * 1. 可以使用#get()一次性请求文件的所有内容,
 * 2. 也可以使用#getInputStream()获取java.io.InputStream InputStream并处理该文件,而不尝试将其加载到内存中,这可能会使用大型文件。
 *
 * 当使用DiskFileItemFactory时,您应该考虑以下几点:
 * 	1.临时文件一旦不再需要就会自动删除。(更准确地说,当对应的{@link java.io.File}实例被垃圾回收时。)
 *  2.这是通过所谓的reaper线程完成的,这个线程在类org.apache.commons.io.FileCleaner被加载时自动启动
 */
public class DiskFileItem implements FileItem, FileItemHeadersSupport {
    private static final long serialVersionUID = 2237570099615271025L;

	// 默认的内容编码
    public static final String DEFAULT_CHARSET = "ISO-8859-1";

    private static final String UID = new java.rmi.server.UID().toString().replace(':', '_').replace('-', '_');

    // 用于唯一标识符的生成
    private static int counter = 0;

    // 浏览器提供的表单字段名
    private String fieldName;

	// 浏览器传递的内容类型,如果未定义为null
    private String contentType;

	// 当前FileItem是否是一个简单的表单字段
    private boolean isFormField;

	// 上传文件的文件名,如果当前FileItem是表单字段为null
    private String fileName;

    // 当前FileItem的大小,单位为字节
    private long size = -1;

    // 阈值,上传文件大小小于此值保存在内存,大于此值缓存到本地
    private int sizeThreshold;

    // 上传的文件将被存储在磁盘上的目录
    private File repository;

    // 文件的缓存内容
    private byte[] cachedContent;

	// 当前FileItem的默认输出流
    private transient DeferredFileOutputStream dfos;

    // 使用的临时文件
    private transient File tempFile;

	// 用于序列化当前FileItem内容的文件
    private File dfosFile;

    // 当前FileItem的header
    private FileItemHeaders headers;

    /**
     * Constructs a new DiskFileItem instance.
     * @param fieldName     表单字段名
     * @param contentType   浏览器传递的内容类型,如果未指定为null
     * @param isFormField   当前FileItem是否是一个表单字段,否则就是一个上传文件
     * @param fileName      上传文件的原始文件名,如果当前FielItem为表单字段,fileName为null
     * @param sizeThreshold 阈值,上传文件大于此值缓存到磁盘,小于就保存在内存
     * @param repository    当上传文件大于sizeThreshold,上传文件缓存到磁盘的文件路径
     */
    public DiskFileItem(String fieldName, String contentType, boolean isFormField, String fileName, int sizeThreshold, File repository) {
        this.fieldName = fieldName;
        this.contentType = contentType;
        this.isFormField = isFormField;
        this.fileName = fileName;
        this.sizeThreshold = sizeThreshold;
        this.repository = repository;
    }
	
	/*
	 * 返回一个用于存储上传文件数据的输出流
	 * FileUploadBase # parseRequest(RequestContext ctx) # Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
	 * 当FileItem刚实例化,dfos为null,调用getOutputStream()获取缓存文件Xxx.tmp,将上传文件缓存到Xxx.tmp中
	 */
    public OutputStream getOutputStream() throws IOException {
        if (dfos == null) {
			// 返回一个用于存储上传文件的临时文件:Xxx.tmp
            File outputFile = getTempFile();
            dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
        }
        return dfos;
    }

    // 返回上传文件的输入流
    public InputStream getInputStream() throws IOException {
        if (!isInMemory()) {// 上传文件缓存在磁盘,打开文件流
            return new FileInputStream(dfos.getFile());
        }
		// 上传文件保存在内存
        if (cachedContent == null) {
        }
        return new ByteArrayInputStream(cachedContent);
    }

    // 从ContenType中解析出内容编码
    public String getCharSet() {
        ParameterParser parser = new ParameterParser();
        parser.setLowerCaseNames(true);
        Map params = parser.parse(getContentType(), ';');
        return (String) params.get("charset");
    }
	
	// 使用默认的编码将文件的内容作为字符串返回
    public String getString() {
        byte[] rawdata = get();
        String charset = getCharSet();
        if (charset == null) {
            charset = DEFAULT_CHARSET;
        }
        try {
            return new String(rawdata, charset);
        } catch (UnsupportedEncodingException e) {
            return new String(rawdata);
        }
    }

	// 使用指定的编码将文件的内容作为字符串返回
    public String getString(final String charset) throws UnsupportedEncodingException {
        return new String(get(), charset);
    }

    /**
	 * 将上传文件的内容作为字节数组返回
	 * 如果上传文件没有缓存在内存中,它们将从磁盘加载并缓存
     */
    public byte[] get() {
        if (isInMemory()) {// 缓存在内存中
            if (cachedContent == null) {
                cachedContent = dfos.getData();
            }
            return cachedContent;
        }
        byte[] fileData = new byte[(int) getSize()];
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(dfos.getFile());
			// 从输入流中读取,存储在fileData中
            fis.read(fileData);
        } catch (IOException e) {
            fileData = null;
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        return fileData;
    }
	
    // 返回上传文件的大小,单位为字节
    public long getSize() {
        if (size >= 0) {
            return size;
        } else if (cachedContent != null) {
            return cachedContent.length;
        } else if (dfos.isInMemory()) {
            return dfos.getData().length;
        } else {
            return dfos.getFile().length();
        }
    }

    /**
	 * 将上传文件写入磁盘的方便方法
     */
    public void write(File file) throws Exception {
        if (isInMemory()) {// 上传文件缓存在内存中
            FileOutputStream fout = null;
            try {
                fout = new FileOutputStream(file);
                fout.write(get());
            } finally {
                if (fout != null) {
                    fout.close();
                }
            }
        } else {// 缓存到磁盘
            File outputFile = getStoreLocation();
            if (outputFile != null) {
                // Save the length of the file
                size = outputFile.length();
                // 上传的文件正在存储在磁盘上的临时位置,以便将其移动到所需的文件。
                if (!outputFile.renameTo(file)) {
                    BufferedInputStream in = null;
                    BufferedOutputStream out = null;
                    try {
                        in = new BufferedInputStream(new FileInputStream(outputFile));
                        out = new BufferedOutputStream(new FileOutputStream(file));
                        IOUtils.copy(in, out);
                    } finally {
                        if (in != null) {
                            try {
                                in.close();
                            } catch (IOException e) {
                                // ignore
                            }
                        }
                        if (out != null) {
                            try {
                                out.close();
                            } catch (IOException e) {
                                // ignore
                            }
                        }
                    }
                }
            } else {
                /*
                 * 无论什么原因,我们无法将文件写入磁盘。
                 */
                throw new FileUploadException("Cannot write uploaded file to disk!");
            }
        }
    }
	
    /**
     * 序列化, 写入此对象的状态
     * @param out 要被写入流的状态
     */
    private void writeObject(ObjectOutputStream out) throws IOException {
        // Read the data
        if (dfos.isInMemory()) {
            cachedContent = get();
        } else {
            cachedContent = null;
            dfosFile = dfos.getFile();
        }
        // write out values
        out.defaultWriteObject();
    }

    // 反序列化,
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        // read values
        in.defaultReadObject();

        OutputStream output = getOutputStream();
        if (cachedContent != null) {// 缓存数据
            output.write(cachedContent);
        } else {
            FileInputStream input = new FileInputStream(dfosFile);
            IOUtils.copy(input, output);
            dfosFile.delete();
            dfosFile = null;
        }
        output.close();
        cachedContent = null;
    }
	
	// 返回一个用于存储上传文件的临时文件
    protected File getTempFile() {
        if (tempFile == null) {
			// 临时文件路劲
            File tempDir = repository;
            if (tempDir == null) {
                tempDir = new File(System.getProperty("java.io.tmpdir"));
            }
			// 临时文件名
            String tempFileName = "upload_" + UID + "_" + getUniqueId() + ".tmp";
			// 临时文件
            tempFile = new File(tempDir, tempFileName);
        }
        return tempFile;
    }
	
    // 删除FileItem的底层存储,包括删除任何关联的临时磁盘文件
    public void delete() {
        cachedContent = null;
        File outputFile = getStoreLocation();
        if (outputFile != null && outputFile.exists()) {
            outputFile.delete();
        }
    }

    // 返回当前FileItem存储在磁盘上的位置,如果存储在内存中,返回null
    public File getStoreLocation() {
        return dfos == null ? null : dfos.getFile();
    }

    // 从临时存储中删除文件内容
    protected void finalize() {
        File outputFile = dfos.getFile();
        if (outputFile != null && outputFile.exists()) {
            outputFile.delete();
        }
    }

    // 返回一个唯一标识符
    private static String getUniqueId() {
        final int limit = 100000000;
        int current;
        synchronized (DiskFileItem.class) {
            current = counter++;
        }
        String id = Integer.toString(current);

        if (current < limit) {
            id = ("00000000" + id).substring(id.length());
        }
        return id;
    }

    public String toString() {
        return "name=" + this.getName()  + ", StoreLocation="  + String.valueOf(this.getStoreLocation())
            + ", size=" + this.getSize()  + "bytes, "  + "isFormField=" + isFormField() + ", FieldName=" + this.getFieldName();
    }

    // 返回浏览器传递的内容类型,如果未指定返回null
    public String getContentType() {
        return contentType;
    }
	
    // 返回上传文件的原始文件名
    public String getName() {
        return fileName;
    }

	// 当前上传文件是否存储在内存中
    public boolean isInMemory() {
        if (cachedContent != null) {
            return true;
        }
        return dfos.isInMemory();
    }

    // 返回字段名
    public String getFieldName() {
        return fieldName;
    }

    // 设置字段名
    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }


    // 返回当前FileItem是否是一个表单字段,false表示是一个上传文件
    public boolean isFormField() {
        return isFormField;
    }

    public void setFormField(boolean state) {
        isFormField = state;
    }

    // 返回当前FileItem的headers
    public FileItemHeaders getHeaders() {
        return headers;
    }

    public void setHeaders(FileItemHeaders pHeaders) {
        headers = pHeaders;
    }
}




  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 安卓源码合集-csdn是一个非常有用的工具和资源网站,提供了大量的开源安卓项目源码供开发者学习和使用。 首先,这个合集汇集了来自开源社区的各种优秀的安卓项目源码。这些源码不仅涵盖了安卓开发的各个方面,如UI界面、网络通信、数据存储等,还有一些特定领域的应用,如音视频处理、图像处理、机器学习等。从这些源码中,开发者可以学习到各种技术和知识,拓宽自己的视野和开发能力。 其次,通过这个合集,开发者可以找到适合自己需求的开源项目源码。不同的项目可能有不同的功能和特点,可以根据自己的需求进行选择和使用。有些源码可能是完整的应用程序,可以直接使用或基于此进行二次开发;有些源码可能是某个功能模块的实现,可以作为学习参考或直接集成到自己的项目中。 此外,这个合集还提供了源码的下载和查看方式。开发者可以通过下载源码来进行学习和使用,也可以在线查看源码进行参考。对于一些比较复杂的项目,还提供了详细的项目文档和使用说明,方便开发者的使用和理解。 总的来说,安卓源码合集-csdn为开发者提供了一个集成了优秀开源项目源码的平台,为开发者学习和使用安卓开发技术提供了便利。无论是初学者还是有一定经验的开发者,都可以在这里找到适合自己的项目源码,提升自己的开发水平和能力。 ### 回答2: 安卓源码合集 -csdn 是一个在 CSDN 上收集和分享安卓开源项目的一个资源合集。CSDN(博客专区)是一个面向IT技术人员的专业社区平台,它提供了丰富的技术资源和社交交流的平台。 安卓源码合集 -csdn 这个资源合集汇集了众多优秀的安卓开源项目,包括应用程序源码、工具类库、框架和示例代码等。通过该合集,开发者们可以快速地找到他们感兴趣的项目,并获取源码用于学习和参考。 这个合集的优点在于它的更新频率高,可以及时收录最新的开源项目。同时,合集中的项目都经过精心筛选,保证了其质量和可靠性。此外,该合集还提供了搜索功能,开发者们可以根据关键词进行搜索,找到适合自己需求的项目。 使用安卓源码合集 -csdn,开发者们可以快速地找到自己需要的安卓开源项目,可以通过学习他人的代码,了解他们的实现思路和技术。对于新手开发者来说,这个合集也可以作为一个学习的平台,他们可以通过阅读和理解他人的代码,提高自己的编码能力。 总之,安卓源码合集 -csdn 是一个非常有用的资源合集,可以帮助开发者们快速找到优秀的安卓开源项目,提高自己的技术水平。无论是初学者还是资深开发者,都可以从中受益。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值