Java+ImageMagic解决UC浏览器保存的GIF无法再次打开问题
程序员文章站
2022-03-24 18:27:39
...
Java+ImageMagic解决UC浏览器保存的GIF无法再次打开问题
使用UC保存的gif动态图,经常就无法再次打开了。
原因:UC保存的图片可能是webp的格式,而很多图片浏览器不支持webp格式。
webp的详细介绍参考百度百科,简单理解一下就是一个压缩率很高的图片压缩格式。
我们使用Java + ImageMagic的方式将webp格式的gif文件重新转换一次,生成标准的gif后就支持打开了。
本文简单粗暴的对所有的gif都进行了转换。有需要的小伙伴请修改handleFile方法,对输入文件进行判断。比如分析文件的头信息,来判断是否需要转换。
具体代码
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.FileChannel;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 将webp格式的动态图转换为GIF
* <p>
* 准备参数:
* <ul>
* <li>inputFolderPath,输入目录</li>
* <li>outputFolderPath,输出目录</li>
* <li>convertFilePath,ImageMagic的Convert命令</li>
* </ul>
* </p>
*
* @auth 羊肉馅大包子
*/
public class Webp2Gif {
/**
* 输入目录
*/
private static final String inputFolderPath = "D:\\delete\\before";
/**
* 输出目录
*/
private static final String outputFolderPath = "D:\\delete\\after";
/**
* 转换命令路径,要指向可以执行的文件。windows下一般后缀为exe
*/
private static final String convertFilePath = "D:\\server\\ImageMagick-7.0.10-Q16\\convert.exe";
/**
* 是否强制覆盖
* true:不管输出文件是否存在,都转换并覆盖
* false:输出文件存在时不处理
*/
private static final boolean forceOverride = true;
public static void main(String[] args) {
printInfo();
doCheck();
try {
scan();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void printInfo() {
System.out.println("###################################");
System.out.println("Power by 羊肉馅大包子");
System.out.println("Update at 2021-02-22");
System.out.println("注意:执行过程中尽量不要修改输入目录");
System.out.println("###################################");
}
private static void scan() throws IOException {
System.out.println("开始处理");
File inputFile = new File(inputFolderPath);
Path path = inputFile.toPath();
int inputNameCount = path.getNameCount();
File outFolder = new File(outputFolderPath);
Files.walkFileTree(path, new FileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (!dir.equals(path)) {
File outFile = new File(outFolder, dir.subpath(inputNameCount, dir.getNameCount()).toString());
if (!outFile.exists() && !outFile.mkdirs()) {
throw new IOException("创建目录失败,目录:" + outFile.getAbsolutePath());
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
// 执行处理
File outFile = new File(outFolder, file.subpath(inputNameCount, file.getNameCount()).toString());
try {
handleFile(file.toFile(), outFile);
} catch (InterruptedException e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
}
private static void handleFile(File inputFile, File outFile) throws IOException, InterruptedException {
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
System.out.println("输入文件:" + inputFile);
System.out.println("输出文件:" + outFile);
if (outFile.exists() && !forceOverride) {
return;
}
System.out.println("开始处理文件:" + inputFile.getAbsolutePath());
if (isWebp(inputFile)) {
try {
ProcessResult processResult = doExecuteCmd(new String[]{convertFilePath, inputFile.getAbsolutePath(), outFile.getAbsolutePath()});
System.out.println("执行结果:" + processResult.returnCode);
System.out.println(processResult.out);
System.out.println(processResult.err);
} catch (ExecutionException e) {
e.printStackTrace();
}
} else {
copyFile(inputFile, outFile);
}
}
private static ProcessResult doExecuteCmd(String[] args) throws IOException, InterruptedException, ExecutionException {
Process exec = Runtime.getRuntime().exec(args);
try {
Future<String> outputFuture = getOutput(exec.getInputStream());
Future<String> errorFuture = getOutput(exec.getErrorStream());
int returnValue = exec.waitFor();
ProcessResult result = new ProcessResult();
result.returnCode = returnValue;
result.out = outputFuture.get();
result.err = errorFuture.get();
return result;
} finally {
exec.destroy();
}
}
/**
* 获取输出
*
* @param outputStream 输出流
* @return 输出字符
*/
private static Future<String> getOutput(final InputStream outputStream) {
return Executors.newCachedThreadPool().submit(() -> {
StringBuilder outputStringBuilder = new StringBuilder();
BufferedReader outputBufferedReader = new BufferedReader(new InputStreamReader(outputStream));
String line;
while ((line = outputBufferedReader.readLine()) != null) {
outputStringBuilder.append(line).append("\n");
}
return outputStringBuilder.toString();
});
}
private static boolean isWebp(File inputFile) {
return inputFile.getName().endsWith(".gif");
}
private static void doCheck() {
System.out.println("开始执行校验...");
// 第一部分检查完成
File inputFolder = new File(inputFolderPath);
if (!inputFolder.exists()) {
throw new IllegalArgumentException("输入目录不存在:" + inputFolderPath);
}
if (!inputFolder.isDirectory()) {
throw new IllegalArgumentException("输入目录不是一个文件夹:" + inputFolderPath);
}
if (!inputFolder.canRead()) {
throw new IllegalArgumentException("输入目录不能读取:" + inputFolderPath);
}
// 第二部分检查完成
File outputFolder = new File(outputFolderPath);
if (outputFolder.exists() && !inputFolder.isDirectory()) {
throw new IllegalArgumentException("输出目录存在,但是不是一个文件夹:" + outputFolder);
}
if (!outputFolder.exists() && !outputFolder.mkdirs()) {
throw new IllegalArgumentException("输出目录创建失败:" + outputFolder);
}
if (!outputFolder.canWrite()) {
throw new IllegalArgumentException("输出目录无写入权限:" + outputFolder);
}
File convert = new File(convertFilePath);
if (!convert.exists()) {
throw new IllegalArgumentException("转换文件不存在," + convertFilePath);
}
if (!convert.canExecute()) {
throw new IllegalArgumentException("转换文件没有执行权限," + convertFilePath);
}
System.out.println("校验完成");
}
/**
* 复制文件
*
* @param sourceFile
* @param destFile
* @return
* @throws IOException
*/
public static void copyFile(File sourceFile, File destFile) throws IOException {
if (!sourceFile.isFile()) {
throw new IOException(sourceFile.getAbsolutePath() + "不是一个标准的文件");
}
if (destFile.exists() && !destFile.isFile()) {
throw new IOException(destFile.getAbsolutePath() + "不是一个标准的文件");
}
File folder = destFile.getParentFile();
if (!folder.exists() && !folder.mkdirs()) {
throw new IOException("创建目录失败,目录:" + folder.getAbsolutePath());
}
if (!destFile.exists() && !destFile.createNewFile()) {
throw new IOException("创建文件失败,文件:" + folder.getAbsolutePath());
}
try (FileInputStream fis = new FileInputStream(sourceFile); FileChannel in = fis.getChannel(); FileOutputStream fos = new FileOutputStream(destFile); FileChannel out = fos.getChannel();) {
out.transferFrom(in, 0, in.size());
final long srcLen = sourceFile.length(); // TODO See IO-386
final long dstLen = destFile.length(); // TODO See IO-386
if (srcLen != dstLen) {
throw new IOException("Failed to copy full contents from '" + sourceFile + "' to '" + destFile + "' Expected length: " + srcLen + " Actual: " + dstLen);
}
destFile.setLastModified(sourceFile.lastModified());
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
public static class ProcessResult {
protected int returnCode;
protected String out;
protected String err;
}
}