145 lines
4.4 KiB
Java
145 lines
4.4 KiB
Java
|
|
package com.ruoyi.warehouse.util;
|
|||
|
|
|
|||
|
|
import java.io.InputStream;
|
|||
|
|
import java.util.Iterator;
|
|||
|
|
import java.util.Locale;
|
|||
|
|
import java.util.Set;
|
|||
|
|
import javax.imageio.ImageIO;
|
|||
|
|
import javax.imageio.ImageReader;
|
|||
|
|
import javax.imageio.stream.ImageInputStream;
|
|||
|
|
import org.apache.commons.io.FilenameUtils;
|
|||
|
|
import org.springframework.web.multipart.MultipartFile;
|
|||
|
|
import com.ruoyi.common.exception.ServiceException;
|
|||
|
|
import com.ruoyi.common.utils.StringUtils;
|
|||
|
|
|
|||
|
|
public final class WarehouseFileUtils
|
|||
|
|
{
|
|||
|
|
private static final long MAX_IMAGE_PIXELS = 40_000_000L;
|
|||
|
|
|
|||
|
|
private WarehouseFileUtils()
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static String validateUpload(MultipartFile file, long maxFileSize, Set<String> allowedExtensions)
|
|||
|
|
{
|
|||
|
|
if (file == null || file.isEmpty())
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("请选择要上传的文件");
|
|||
|
|
}
|
|||
|
|
if (file.getSize() > maxFileSize)
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("文件不能超过" + (maxFileSize / 1024 / 1024) + "MB");
|
|||
|
|
}
|
|||
|
|
String extension = StringUtils.defaultString(FilenameUtils.getExtension(file.getOriginalFilename()))
|
|||
|
|
.toLowerCase(Locale.ROOT);
|
|||
|
|
if (!allowedExtensions.contains(extension))
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("不支持的文件类型");
|
|||
|
|
}
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
byte[] header;
|
|||
|
|
try (InputStream input = file.getInputStream())
|
|||
|
|
{
|
|||
|
|
header = input.readNBytes(12);
|
|||
|
|
}
|
|||
|
|
if ("xls".equals(extension) && !startsWith(header, 0xD0, 0xCF, 0x11, 0xE0))
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("文件内容不是有效的Excel文件");
|
|||
|
|
}
|
|||
|
|
if ("xlsx".equals(extension) && !startsWith(header, 0x50, 0x4B))
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("文件内容不是有效的Excel文件");
|
|||
|
|
}
|
|||
|
|
if (isImage(extension))
|
|||
|
|
{
|
|||
|
|
validateImage(file, extension, header);
|
|||
|
|
}
|
|||
|
|
return extension;
|
|||
|
|
}
|
|||
|
|
catch (ServiceException e)
|
|||
|
|
{
|
|||
|
|
throw e;
|
|||
|
|
}
|
|||
|
|
catch (Exception e)
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("无法读取上传文件,请确认文件未损坏");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void validateImage(MultipartFile file, String extension, byte[] header) throws Exception
|
|||
|
|
{
|
|||
|
|
if ("webp".equals(extension))
|
|||
|
|
{
|
|||
|
|
if (!matchesAscii(header, 0, "RIFF") || !matchesAscii(header, 8, "WEBP"))
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("文件内容不是有效的WebP图片");
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
try (ImageInputStream input = ImageIO.createImageInputStream(file.getInputStream()))
|
|||
|
|
{
|
|||
|
|
if (input == null)
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("文件内容不是有效图片");
|
|||
|
|
}
|
|||
|
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
|||
|
|
if (!readers.hasNext())
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("文件内容不是有效图片");
|
|||
|
|
}
|
|||
|
|
ImageReader reader = readers.next();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
reader.setInput(input, true, true);
|
|||
|
|
long pixels = (long) reader.getWidth(0) * reader.getHeight(0);
|
|||
|
|
if (pixels <= 0 || pixels > MAX_IMAGE_PIXELS)
|
|||
|
|
{
|
|||
|
|
throw new ServiceException("图片尺寸过大,最大支持4000万像素");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
reader.dispose();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static boolean isImage(String extension)
|
|||
|
|
{
|
|||
|
|
return Set.of("jpg", "jpeg", "png", "bmp", "webp").contains(extension);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static boolean startsWith(byte[] source, int... prefix)
|
|||
|
|
{
|
|||
|
|
if (source.length < prefix.length)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
for (int i = 0; i < prefix.length; i++)
|
|||
|
|
{
|
|||
|
|
if ((source[i] & 0xFF) != prefix[i])
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static boolean matchesAscii(byte[] source, int offset, String expected)
|
|||
|
|
{
|
|||
|
|
if (source.length < offset + expected.length())
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
for (int i = 0; i < expected.length(); i++)
|
|||
|
|
{
|
|||
|
|
if (source[offset + i] != (byte) expected.charAt(i))
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|