FileAwareMultipartFile.java
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bremersee.web.multipart;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.core.io.AbstractResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.NonNull;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* The file aware multipart file.
*
* @author Christian Bremer
*/
@EqualsAndHashCode(doNotUseGetters = true)
@ToString(doNotUseGetters = true)
public class FileAwareMultipartFile implements MultipartFile {
private final File file;
private final String parameterName;
private final String originalFilename;
private final String contentType;
private FileAwareMultipartFile() {
this.file = null;
this.parameterName = null;
this.originalFilename = null;
this.contentType = null;
}
/**
* Instantiates a new file aware multipart file.
*
* @param multipartFile the multipart file
* @throws IOException the io exception
*/
public FileAwareMultipartFile(MultipartFile multipartFile) throws IOException {
this(multipartFile, (File) null);
}
/**
* Instantiates a new file aware multipart file.
*
* @param multipartFile the multipart file
* @param tmpDir the tmp dir
* @throws IOException the io exception
*/
public FileAwareMultipartFile(MultipartFile multipartFile, String tmpDir) throws IOException {
this(multipartFile, getTmpDir(tmpDir));
}
/**
* Instantiates a new file aware multipart file.
*
* @param multipartFile the multipart file
* @param tmpDir the tmp dir
* @throws IOException the io exception
*/
public FileAwareMultipartFile(MultipartFile multipartFile, File tmpDir) throws IOException {
if (multipartFile == null) {
this.file = null;
this.parameterName = null;
this.originalFilename = null;
this.contentType = null;
} else {
if (multipartFile.isEmpty()) {
this.file = null;
} else {
if (multipartFile instanceof FileAwareMultipartFile) {
this.file = ((FileAwareMultipartFile) multipartFile).file;
} else {
this.file = getTmpFile(getTmpDir(tmpDir));
FileCopyUtils.copy(
multipartFile.getInputStream(),
Files.newOutputStream(
this.file.toPath(),
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING));
}
}
this.parameterName = multipartFile.getName();
this.originalFilename = multipartFile.getOriginalFilename();
this.contentType = multipartFile.getContentType();
}
}
/**
* Instantiates a new File aware multipart file.
*
* @param inputStream the input stream
* @param parameterName the parameter name
* @param originalFilename the original filename
* @param contentType the content type
* @throws IOException the io exception
*/
public FileAwareMultipartFile(
InputStream inputStream,
String parameterName,
String originalFilename,
String contentType) throws IOException {
this(inputStream, (File) null, parameterName, originalFilename, contentType);
}
/**
* Instantiates a new File aware multipart file.
*
* @param inputStream the input stream
* @param tmpDir the tmp dir
* @param parameterName the parameter name
* @param originalFilename the original filename
* @param contentType the content type
* @throws IOException the io exception
*/
public FileAwareMultipartFile(
InputStream inputStream,
String tmpDir,
String parameterName,
String originalFilename,
String contentType) throws IOException {
this(inputStream, getTmpDir(tmpDir), parameterName, originalFilename, contentType);
}
/**
* Instantiates a new File aware multipart file.
*
* @param inputStream the input stream
* @param tmpDir the tmp dir
* @param parameterName the parameter name
* @param originalFilename the original filename
* @param contentType the content type
* @throws IOException the io exception
*/
public FileAwareMultipartFile(
InputStream inputStream,
File tmpDir,
String parameterName,
String originalFilename,
String contentType) throws IOException {
if (inputStream != null) {
this.file = getTmpFile(tmpDir);
FileCopyUtils.copy(
inputStream,
Files.newOutputStream(
this.file.toPath(),
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING));
} else {
this.file = null;
}
this.parameterName = parameterName;
this.originalFilename = originalFilename;
this.contentType = contentType;
}
/**
* Instantiates a new File aware multipart file.
*
* @param file the file
* @param parameterName the parameter name
* @param originalFilename the original filename
* @param contentType the content type
*/
public FileAwareMultipartFile(
Path file,
String parameterName,
String originalFilename,
String contentType) {
this.file = file != null ? file.toFile() : null;
this.parameterName = parameterName;
this.originalFilename = originalFilename;
this.contentType = contentType;
}
/**
* Instantiates a new File aware multipart file.
*
* @param file the file
* @param parameterName the parameter name
* @param originalFilename the original filename
* @param contentType the content type
*/
public FileAwareMultipartFile(
File file,
String parameterName,
String originalFilename,
String contentType) {
this.file = file;
this.parameterName = parameterName;
this.originalFilename = originalFilename;
this.contentType = contentType;
}
/**
* Empty file aware multipart file.
*
* @return the file aware multipart file
*/
public static FileAwareMultipartFile empty() {
return new FileAwareMultipartFile();
}
/**
* Delete.
*
* @param multipartFile the multipart file
*/
public static void delete(MultipartFile multipartFile) {
if (multipartFile != null && multipartFile.getResource().isFile()) {
try {
Files.delete(multipartFile.getResource().getFile().toPath());
} catch (Exception ignored) {
// ignored
}
}
}
private static File getTmpDir(String tmpDir) {
return getTmpDir(StringUtils.hasText(tmpDir) ? new File(tmpDir) : null);
}
private static File getTmpDir(File tmpDir) {
return Optional.ofNullable(tmpDir)
.filter(dir -> dir.exists() && dir.isDirectory() && dir.canRead() && dir.canWrite())
.orElseGet(() -> new File(System.getProperty("java.io.tmpdir")));
}
private static File getTmpFile(File tmpDir) throws IOException {
return File.createTempFile("uploaded-", ".tmp", tmpDir);
}
@NonNull
@Override
public String getName() {
return parameterName == null ? "" : parameterName;
}
@Override
public String getOriginalFilename() {
return !StringUtils.hasText(originalFilename) && file != null
? file.getName()
: originalFilename;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return getSize() == 0;
}
@Override
public long getSize() {
return isFileValid() ? file.length() : 0;
}
@NonNull
@Override
public byte[] getBytes() throws IOException {
return isEmpty() ? new byte[0] : FileCopyUtils.copyToByteArray(new FileInputStream(file));
}
@NonNull
@Override
public InputStream getInputStream() throws IOException {
return isEmpty() ? new ByteArrayInputStream(new byte[0]) : new FileInputStream(file);
}
@NonNull
@Override
public Resource getResource() {
return isEmpty() ? new EmptyResource() : new FileSystemResource(file);
}
@Override
public void transferTo(@NonNull File dest) throws IOException, IllegalStateException {
if (isFileValid()) {
FileCopyUtils.copy(file, dest);
}
}
private boolean isFileValid() {
return file != null && file.exists() && file.isFile() && file.canRead();
}
@SuppressWarnings("Lombok")
@EqualsAndHashCode
private static class EmptyResource extends AbstractResource {
private EmptyResource() {
}
@NonNull
@Override
public String getDescription() {
return "Empty resource.";
}
@NonNull
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(new byte[0]);
}
@NonNull
@Override
public String toString() {
return getDescription();
}
}
}