java文件上传的时候发现CommonsMultipartFile的getInputStream方法在文件不同大小的时候返回的流对象是不同的,导致CommonsMultipartFile.getInputStream()方法在转换为对应的子类对象是报错,当时很是纠结,然后看了下getInputStream的代码实现
结论:
源码实现:
下面看一下具体的内部实现:
@Override
public InputStream getInputStream() throws IOException {
if (!isAvailable()) {
throw new IllegalStateException("File has been moved - cannot be read again");
}
// 实际返回的对象是 成员变量fileItem返回的
InputStream inputStream = this.fileItem.getInputStream();
return (inputStream != null ? inputStream : StreamUtils.emptyInput());
}
成员变量FileItem的具体实现子类是DiskFileItem,下面是DiskFileItem的getInputStream:
public InputStream getInputStream()
throws IOException {
// 判断文件的大小是
if (!isInMemory()) {
return new FileInputStream(dfos.getFile());
}
if (cachedContent == null) {
cachedContent = dfos.getData();
}
return new ByteArrayInputStream(cachedContent);
}
isInMemory方法的具体实现:
public boolean isInMemory() {
if (cachedContent != null) {
return true;
}
// 继续判断
return dfos.isInMemory();
}
public boolean isInMemory()
{
return !isThresholdExceeded();
}
public boolean isThresholdExceeded()
{
// 最终的判断逻辑 written指的是上传的文件的大小,threshold是判断文件大小的标准
return written > threshold;
}
接下来我们需要看一下threshold是在什么地方进行赋值的:
// 构造方法传值
public ThresholdingOutputStream(int threshold)
{
this.threshold = threshold;
}
// DeferredFileOutputStream是ThresholdingOutputStream的子类
private DeferredFileOutputStream(int threshold, File outputFile, String prefix, String suffix, File directory) {
// 当DeferredFileOutputStream进行构造初始化的时候,首先会调用父类的构造方法
super(threshold);
this.outputFile = outputFile;
memoryOutputStream = new ByteArrayOutputStream();
currentOutputStream = memoryOutputStream;
this.prefix = prefix;
this.suffix = suffix;
this.directory = directory;
}
public DeferredFileOutputStream(int threshold, File outputFile)
{
this(threshold, outputFile, null, null, null);
}
public OutputStream getOutputStream()
throws IOException {
if (dfos == null) {
File outputFile = getTempFile();
// DeferredFileOutputStream实例化的地方,成员变量threshold的值就是DiskFileItem的成员变量sizeThreshold的值,而sizeThreshold也是通过DiskFileItem的构造方法进行赋值的
dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
}
return dfos;
}
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;
}
// DiskFileItem的初始化是DiskFileItemFactory工厂实例化的,下面就是DiskFileItemFactory的createItem方法实现
public FileItem createItem(String fieldName, String contentType,
boolean isFormField, String fileName) {
DiskFileItem result = new DiskFileItem(fieldName, contentType,
isFormField, fileName, sizeThreshold, repository);
FileCleaningTracker tracker = getFileCleaningTracker();
if (tracker != null) {
tracker.track(result.getTempFile(), result);
}
return result;
}
我们整理一下具体的流程:
上面整个流程就是将DiskFileItemFactory成员变量sizeThreshold的值赋值给ThresholdingOutputStream成员变量threshold,那么DiskFileItemFactory中sizeThreshold的值是什么呢
private int sizeThreshold = DEFAULT_SIZE_THRESHOLD;
// 这里我们可以看到默认值就是10240b = 10kb
public static final int DEFAULT_SIZE_THRESHOLD = 10240;