@@ -67,9 +67,15 @@ private String generateTempImagePath(String username) {
6767 * @param file File
6868 */
6969 public void generateThumbnail (File file , FileDocument fileDocument ) {
70- try {
71- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream ();
72- cropImage (new FileInputStream (file ), "1" , "256" , "256" , byteArrayOutputStream );
70+ try (FileInputStream fileInputStream = new FileInputStream (file );
71+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream ()) {
72+ String extName = FileUtil .extName (file );
73+ if ("gif" .equalsIgnoreCase (extName )) {
74+ // GIF 图片单独处理,保持动画效果
75+ cropImage (fileInputStream , "1" , "256" , "256" , byteArrayOutputStream , "gif" );
76+ } else {
77+ cropImage (fileInputStream , "1" , "256" , "256" , byteArrayOutputStream , "png" );
78+ }
7379 if (dataSourceProperties .getType () == DataSourceType .mongodb ) {
7480 fileDocument .setContent (byteArrayOutputStream .toByteArray ());
7581 } else {
@@ -155,6 +161,21 @@ public static void replaceWebp(File srcFile, File destFile, boolean deleteSrc) {
155161 * @param outputStream 输出流,用于接收处理后的 png 图片数据
156162 */
157163 public static void cropImage (InputStream inputStream , String q , String w , String h , OutputStream outputStream ) {
164+ cropImage (inputStream , q , w , h , outputStream , null );
165+ }
166+
167+ /**
168+ * 使用单一的 ImageMagick 命令,从 InputStream 对图片进行条件性缩放和质量调整。
169+ * 只有当图片尺寸大于目标尺寸时,才会进行缩放。
170+ *
171+ * @param inputStream 源图片输入流
172+ * @param q 质量 (字符串 "0.0" 到 "1.0", 默认 0.8)
173+ * @param w 目标最大宽度 (字符串)
174+ * @param h 目标最大高度 (字符串, 可选)
175+ * @param outputStream 输出流,用于接收处理后的 png 图片数据
176+ * @param outputFormat 输出格式 (png 和 gif)
177+ */
178+ public static void cropImage (InputStream inputStream , String q , String w , String h , OutputStream outputStream , String outputFormat ) {
158179 // 1. 解析输入参数
159180 double quality = parseQuality (q );
160181 int targetWidth = parseDimension (w );
@@ -167,9 +188,17 @@ public static void cropImage(InputStream inputStream, String q, String w, String
167188 // IoUtil.copy(inputStream, outputStream);
168189 return ;
169190 }
191+ if (outputFormat == null ) {
192+ outputFormat = "png" ;
193+ }
170194
171195 // 2. 构建单一的、条件性的 ImageMagick 命令
172- CommandLine cmdLine = buildConditionalResizeCommand (targetWidth , targetHeight , quality );
196+ CommandLine cmdLine ;
197+ if ("gif" .equals (outputFormat )) {
198+ cmdLine = buildConditionalResizeGIFCommand (targetWidth , targetHeight );
199+ } else {
200+ cmdLine = buildConditionalResizeCommand (targetWidth , targetHeight , quality );
201+ }
173202
174203 // 3. 执行命令,将输入流管道连接到命令的标准输入
175204 try {
@@ -217,6 +246,37 @@ private static CommandLine buildConditionalResizeCommand(int targetWidth, int ta
217246 return cmdLine ;
218247 }
219248
249+ /**
250+ * 构建一个使用条件缩放的 ImageMagick 命令行,专门用于处理 GIF 图片以保持动画效果。
251+ * @param targetWidth 目标宽度
252+ * @param targetHeight 目标高度 (如果<0则忽略)
253+ * @return 构建好的命令行对象
254+ */
255+ private static CommandLine buildConditionalResizeGIFCommand (int targetWidth , int targetHeight ) {
256+ CommandLine cmdLine = new CommandLine ("magick" );
257+
258+ cmdLine .addArgument ("-" , false );
259+
260+ cmdLine .addArgument ("-coalesce" , false );
261+ cmdLine .addArgument ("-resize" , false );
262+
263+ StringBuilder geometry = new StringBuilder ();
264+ geometry .append (targetWidth );
265+ if (targetHeight > 0 ) {
266+ geometry .append ("x" ).append (targetHeight );
267+ }
268+ geometry .append (">" );
269+
270+ cmdLine .addArgument (geometry .toString (), false );
271+
272+ cmdLine .addArgument ("-layers" , false );
273+ cmdLine .addArgument ("optimize" , false );
274+
275+ cmdLine .addArgument ("gif:-" , false );
276+
277+ return cmdLine ;
278+ }
279+
220280 /**
221281 * <p>获取图片尺寸</p>
222282 * 命令: identify -format "%w %h" image.jpg
@@ -225,18 +285,25 @@ private static CommandLine buildConditionalResizeCommand(int targetWidth, int ta
225285 * @return 一个包含 [width, height] 的数组,如果失败则返回 null
226286 */
227287 public static ImageFormat identifyFormat (File imageFile ) {
228- ByteArrayOutputStream outputStream = new ByteArrayOutputStream ();
229-
230- CommandLine cmdLine = new CommandLine ("magick" );
231- cmdLine .addArgument ("identify" , false );
232- cmdLine .addArgument ("-format" , false );
233- cmdLine .addArgument (ImageFormat .DEFAULT_FORMAT_PARAM , false );
234- cmdLine .addArgument (imageFile .getAbsolutePath (), false );
235- CommandUtil .execCommand (cmdLine , null , outputStream );
236-
237- String output = IoUtil .toStr (outputStream , StandardCharsets .UTF_8 );
288+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream ()) {
289+ CommandLine cmdLine = new CommandLine ("magick" );
290+ cmdLine .addArgument ("identify" , false );
291+ cmdLine .addArgument ("-format" , false );
292+ cmdLine .addArgument (ImageFormat .DEFAULT_FORMAT_PARAM , false );
293+ cmdLine .addArgument (imageFile .getAbsolutePath (), false );
294+ CommandUtil .execCommand (cmdLine , null , outputStream );
295+
296+ String output = IoUtil .toStr (outputStream , StandardCharsets .UTF_8 );
297+ if (CharSequenceUtil .isBlank (output )) {
298+ log .error ("ImageMagick identify command returned empty output for file: {}" , imageFile .getAbsolutePath ());
299+ throw new CommonException (ExceptionType .SYSTEM_ERROR .getCode (), "获取图片信息失败: identify 命令返回为空" );
300+ }
301+ return ImageFormat .getImageFormat (output );
302+ } catch (IOException e ) {
303+ log .error ("Failed to identify image format: {}" , imageFile .getAbsolutePath (), e );
304+ throw new CommonException (ExceptionType .SYSTEM_ERROR .getCode (), "获取图片信息失败" );
305+ }
238306
239- return ImageFormat .getImageFormat (output );
240307 }
241308
242309 @ Builder
0 commit comments