MultipartFile:底层实现原理MultipartFile是⼀个接⼝,实现类是CommonsMultipartFile,
public class CommonsMultipartFile implements MultipartFile, Serializable {
protected static final Log logger = Log(CommonsMultipartFile.class);
private final FileItem fileItem;//看到这个FileItem,我就知道还有下⼀层/(ㄒoㄒ)/~~,CommonsMultipartFile就是对fileTiem做了封装。
private final long size;
private boolean preserveFilename = false;
/**
* Create an instance wrapping the given FileItem.
* @param fileItem the FileItem to wrap
*/
public CommonsMultipartFile(FileItem fileItem) {
this.fileItem = fileItem;//从这就可有看出是对fileIem做了封⾯封装
this.size = Size();
}
好吧,继续向下FileItem的源码,然后就会发现FileItem是⼀个接⼝,它的实现类是DiskFileItem。
下⾯是Diskfiletem的源码
package org.apachemons.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.apachemons.fileupload.FileItem;
import org.apachemons.fileupload.FileItemHeaders;
import org.apachemons.fileupload.FileItemHeadersSupport;
import org.apachemons.fileupload.FileUploadException;
import org.apachemons.fileupload.ParameterParser;
import org.apachemons.io.IOUtils;
import org.apachemons.io.output.DeferredFileOutputStream;
/**
* 接⼝org.apachemons.fileupload.FileItem FileItem的默认实现
* 从org.apachemons.fileupload.DiskFileUpload实例检索此类的实例后,
inputtypefile不上传文件* 1. 可以使⽤#get()⼀次性请求⽂件的所有内容,
* 2. 也可以使⽤#getInputStream()获取java.io.InputStream InputStream并处理该⽂件,⽽不尝试将其加载到内存中,这可能会使⽤⼤型⽂件。 *
* 当使⽤DiskFileItemFactory时,您应该考虑以下⼏点:
*  1.临时⽂件⼀旦不再需要就会⾃动删除。(更准确地说,当对应的{@link java.io.File}实例被垃圾回收时。)
*  2.这是通过所谓的reaper线程完成的,这个线程在类org.apachemons.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 = i.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.isFormField = isFormField;
this.fileName = fileName;
this.sizeThreshold = sizeThreshold;
}
/*
* 返回⼀个⽤于存储上传⽂件数据的输出流
* FileUploadBase # parseRequest(RequestContext ctx) # py(item.openStream(), OutputStream(), true);
* 当FileItem刚实例化,dfos为null,调⽤getOutputStream()获取缓存⽂件p,将上传⽂件缓存到p中
*/
public OutputStream getOutputStream() throws IOException {
if (dfos == null) {
// 返回⼀个⽤于存储上传⽂件的临时⽂件:p
File outputFile = getTempFile();
dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
}
return dfos;
}
// 返回上传⽂件的输⼊流
public InputStream getInputStream() throws IOException {
if (!isInMemory()) {// 上传⽂件缓存在磁盘,打开⽂件流
return new File());
}
// 上传⽂件保存在内存
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) ("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 = Data();
}
return cachedContent;
}
byte[] fileData = new byte[(int) getSize()];
FileInputStream fis = null;
try {
fis = new File());
// 从输⼊流中读取,存储在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()) {
Data().length;
} else {
File().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 (!ameTo(file)) {
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(outputFile));
out = new BufferedOutputStream(new FileOutputStream(file));
} 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 = File();
}
// 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);
dfosFile.delete();
dfosFile = null;
}
output.close();
cachedContent = null;
}
// 返回⼀个⽤于存储上传⽂件的临时⽂件
protected File getTempFile() {
if (tempFile == null) {
// 临时⽂件路劲
File tempDir = repository;
if (tempDir == null) {
tempDir = new Property("pdir"));
}
// 临时⽂件名
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 && ists()) {
outputFile.delete();
}
}
// 返回当前FileItem存储在磁盘上的位置,如果存储在内存中,返回null
public File getStoreLocation() {
return dfos == null ? null : File();
}
// 从临时存储中删除⽂件内容
protected void finalize() {
File outputFile = File();
if (outputFile != null && ists()) {
outputFile.delete();
}
}
// 返回⼀个唯⼀标识符
private static String getUniqueId() {
final int limit = 100000000;
int current;
synchronized (DiskFileItem.class) {
current = counter++;
}
String id = String(current);
if (current < limit) {
id = ("00000000" + id).substring(id.length());
}
return id;
}
public String toString() {
return "name=" + Name()  + ", StoreLocation="  + String.StoreLocation())
+ ", size=" + Size()  + "bytes, "  + "isFormField=" + isFormField() + ", FieldName=" + FieldName();
}
// 返回浏览器传递的内容类型,如果未指定返回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;
}
}
到源码就很多疑惑就解开了,⽐如MultpatrFile是怎么返回字节数组的,⼀步步下来就会发现其实是调⽤了 DiskFileItem的get()⽅法⽽get()的实现原理是利⽤FileInputStream接⼝,⽽FileInputStream的底层是native⽅法说明有实现类但不是java类
public byte[] get() {
if (isInMemory()) {// 缓存在内存中
if (cachedContent == null) {
cachedContent = Data();
}
return cachedContent;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。