`
frank1998819
  • 浏览: 758347 次
  • 性别: Icon_minigender_1
  • 来自: 南京
文章分类
社区版块
存档分类

Java 压缩图片并打成ZIP(转)

    博客分类:
  • Java
 
阅读更多

引用

  JAVA 获取网络图片或本地图片压缩后打成ZIP,但是获取网络流存在问题:每次获取图片流的大小不一样(图片不完整),以致无法构建图片进行压缩?

Java代码  收藏代码

    /*
    释以下代码:即可获取完整图片流网络不稳定情况且网络流是顺序读取,所以获得前部份流,不需要关闭连接,只需要将用完的流关闭即可
    */ 
    finally{ 
        if(httpCon != null) 
        httpCon.disconnect(); 
    } 


Java代码  收藏代码

    package com.sunshine.monitor.comm.util.http; 
     
    import java.io.File; 
    import java.io.FileOutputStream; 
    import java.io.IOException; 
    import java.io.InputStream; 
    import java.io.OutputStream; 
    import java.net.HttpURLConnection; 
    import java.net.URL; 
    import java.net.URLConnection; 
    import java.util.Iterator; 
     
    import javax.imageio.ImageIO; 
    import javax.imageio.ImageReader; 
    import javax.imageio.stream.ImageInputStream; 
     
    import sun.net.www.protocol.ftp.FtpURLConnection; 
     
    /**
     * 获取网络图片
     * 
     * @author OY
     */ 
    public abstract class HttpHelpers { 
     
        private static final String KEY = "file.encoding"; 
     
        private static final String ENCODING = "GBK"; 
     
        public static InputStream getInputStream(String url) throws Exception{ 
            URLConnection con = null; 
            HttpURLConnection httpCon = null; 
            FtpURLConnection ftpCon= null; 
            try { 
                System.setProperty(KEY, ENCODING); 
                URL _url = new URL(url); 
                con = _url.openConnection(); 
                con.setConnectTimeout(3000); 
                con.setUseCaches(false);// 不缓存 
                con.setDefaultUseCaches(false); 
                if (con instanceof HttpURLConnection) { 
                    httpCon = (HttpURLConnection) con; 
                    httpCon.setInstanceFollowRedirects(true); 
                    //httpCon.setRequestProperty("Accept-Charset", "utf-8"); 
                    if (httpCon.getResponseCode() >= 300) { 
                        System.out.println("URL:" + url 
                                + ",HTTP Request is not success, Response code is " 
                                + httpCon.getResponseCode()); 
                    } else { 
                        return httpCon.getInputStream(); 
                    } 
                } else if(con instanceof FtpURLConnection){ 
                    ftpCon = (FtpURLConnection)con; 
                    return ftpCon.getInputStream(); 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            }finally{ 
                if(httpCon != null) 
                    httpCon.disconnect(); 
            } 
            return null; 
        } 
         
        public static void main(String[] args) { 
            // 1图片本地存储大小 
            OutputStream fout = null; 
            InputStream input = null; 
            try { 
                fout = new FileOutputStream("F:" + File.separator + "1.jpg"); 
                input = getInputStream("http://192.168.1.200/t.jpg"); 
                byte[] buffer = new byte[1024]; 
                int count = 0 ; 
                while((count=input.read(buffer)) != -1){ 
                    fout.write(buffer, 0, count); 
                } 
                fout.flush(); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally{ 
                try { 
                    if(input != null) input.close(); 
                    if(fout != null) fout.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
             
            // 2是否可以构建图片 
            try { 
                input = getInputStream("http://192.168.1.200/t.jpg"); 
                ImageInputStream iis = ImageIO.createImageInputStream(input); 
                if(iis != null) { 
                    Iterator<ImageReader> it = ImageIO.getImageReaders(iis); 
                    if(!it.hasNext()){ 
                        System.out.println("流不完整或不是图片!"); 
                    } else { 
                        System.out.println(it.next().getFormatName()); 
                    } 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } 
        } 
    } 



引用

图片压缩采用thumbnailator,可以按大小、按比例、按质量压缩并增加水印,API简单

Java代码  收藏代码

     package com.sunshine.monitor.comm.util.compress; 
     
    import java.io.ByteArrayInputStream; 
    import java.io.ByteArrayOutputStream; 
    import java.io.File; 
    import java.io.FileInputStream; 
    import java.io.IOException; 
    import java.io.InputStream; 
     
    import javax.imageio.ImageIO; 
     
    import net.coobird.thumbnailator.Thumbnails; 
    import net.coobird.thumbnailator.geometry.Positions; 
     
    /**
     * 图片压缩:按大小、按比例压缩、按质量
     * 增加水印
     * @author OY
     * 
     */ 
    public abstract class CompressPictureTools { 
     
        private static float QUALITY = 0.6f; 
         
        /**
         * 按大小缩小
         * 
         * @param file
         * @param width
         * @param height
         * @return
         * @throws Exception
         */ 
        public static byte[] compressOfSize(File file, int width, int height) 
                throws Exception { 
            byte[] bs = null; 
            InputStream input = null; 
            try { 
                input = new FileInputStream(file); 
                bs = compressOfSize(input, width, height); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (input != null) 
                        input.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return bs; 
        } 
     
        /**
         * 按大小缩小
         * 
         * @param input 原图
         * @param width 目标宽席
         * @param height 目标高度
         * @return
         * @throws Exception
         */ 
        public static byte[] compressOfSize(InputStream input, int width, int height) 
                throws Exception { 
            ByteArrayOutputStream output = null; 
            try { 
                output = new ByteArrayOutputStream(); 
                Thumbnails.of(input).size(width, height).toOutputStream(output); 
                return output.toByteArray(); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (output != null) 
                        output.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return null; 
        } 
     
        /**
         * 按指定比例进行缩小和放大: percent=1不变 percent>1放大 percent<1缩小
         * 
         * @param input 原图
         * @param percent 压缩比例
         * @return
         * @throws Exception
         */ 
        public static byte[] compressOfPercent(InputStream input, float percent) 
                throws Exception { 
            ByteArrayOutputStream output = null; 
            try { 
                output = new ByteArrayOutputStream(); 
                Thumbnails.of(input).scale(percent).toOutputStream(output); 
                return output.toByteArray(); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (output != null) 
                        output.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return null; 
        } 
     
        /**
         * 按指定比例进行缩小和放大: percent=1不变 percent>1放大 percent<1缩小
         * 
         * @param file 原图
         * @param percent 压缩比例
         */ 
        public static byte[] compressOfPercent(File file, float percent) 
                throws Exception { 
            byte[] bs = null; 
            InputStream input = null; 
            try { 
                input = new FileInputStream(file); 
                bs = compressOfPercent(input, percent); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (input != null) 
                        input.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return bs; 
        } 
     
        /**
         * 按质量压缩:图片尺寸不变,压缩图片文件大小
         * 
         * @param file 原图
         * @param quality
         *            =1为最高质量
         * @return
         * @throws Exception
         */ 
        public static byte[] compressOfQuality(File file, float quality) 
                throws Exception { 
            byte[] bs = null; 
            InputStream input = null; 
            try { 
                input = new FileInputStream(file); 
                bs = compressOfQuality(input, quality); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (input != null) 
                        input.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return bs; 
        } 
     
        /**
         * 按质量压缩:图片尺寸不变,压缩图片文件大小
         * 
         * @param input 原图
         * @param quality
         *            =1为最高质量
         * @return
         * @throws Exception
         */ 
        public static byte[] compressOfQuality(InputStream input, float quality) 
                throws Exception { 
            ByteArrayOutputStream output = null; 
            try { 
                output = new ByteArrayOutputStream(); 
                if(quality == 0){ 
                    Thumbnails.of(input).scale(1f).outputQuality(QUALITY) 
                    .toOutputStream(output); 
                } else { 
                    Thumbnails.of(input).scale(1f).outputQuality(quality) 
                        .toOutputStream(output); 
                } 
                return output.toByteArray(); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (output != null) 
                        output.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return null; 
        } 
     
        /**
         * 图片右下角添加水印
         * 
         * @param fromPic
         *            原图
         * @param markPic
         *            水印图
         * @param transparent
         *            透明度
         * @return
         * @throws Exception
         */ 
        public static byte[] waterMark(byte[] fromPic, InputStream markPic, 
                float transparent) throws Exception { 
            InputStream finput = null; 
            ByteArrayOutputStream output = null; 
            try { 
                finput = new ByteArrayInputStream(fromPic); 
                output = new ByteArrayOutputStream(); 
                Thumbnails 
                        .of(finput) 
                        .scale(1f) 
                        .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(markPic), 
                                transparent).toOutputStream(output); 
                return output.toByteArray(); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (output != null) 
                        output.close(); 
                    if (finput != null) 
                        finput.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return null; 
        } 
     
        /**
         * 图片格式转换
         * 
         * @param fromPic
         *            原图
         * @param picFormat
         *            格式 png,jpg...
         * @return
         * @throws Exception
         */ 
        public static byte[] transferPicFormat(byte[] fromPic, String picFormat) 
                throws Exception { 
            ByteArrayInputStream finput = null; 
            ByteArrayOutputStream output = null; 
            try { 
                finput = new ByteArrayInputStream(fromPic); 
                output = new ByteArrayOutputStream(); 
                Thumbnails.of(finput).outputFormat(picFormat) 
                        .toOutputStream(output); 
                return output.toByteArray(); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (output != null) 
                        output.close(); 
                    if (finput != null) 
                        finput.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            return null; 
        } 
    } 


引用

因JDK1.7以下,不可以设置编码,以致中文乱码问题,未采用java.util.ZipOutputStream,而是Apache ant下的ZipOutputStream

Java代码  收藏代码

    package com.sunshine.monitor.comm.util.compress; 
     
    import java.io.BufferedInputStream; 
    import java.io.File; 
    import java.io.FileInputStream; 
    import java.io.IOException; 
    import java.io.InputStream; 
    import java.io.OutputStream; 
    import java.util.Arrays; 
    import java.util.Collections; 
    import java.util.Iterator; 
    import java.util.List; 
    import java.util.Map; 
    import java.util.Map.Entry; 
    import java.util.Set; 
     
    import org.apache.tools.zip.ZipEntry; 
    import org.apache.tools.zip.ZipOutputStream; 
     
    import com.sunshine.monitor.comm.util.http.HttpHelpers; 
    /**
     * 图片压缩成ZIP,支持并发多线程;
     * java.util.ZipOutputStream中文乱码 
     * 方法一、JDK1.7可以设置编码 
     * 方法二、换成Apache ant
     * @author OY
     * 
     */ 
    public class PicturePackZipTools { 
     
    private static String DEFAULT_COMPRESS_ENCODE = "GBK"; 
         
        private static ZipOutputStream getZipStreamEncode(OutputStream output, 
                String encode) { 
            ZipOutputStream zipStream = new ZipOutputStream(output); 
            if (encode == null || "".equals(encode)) { 
                zipStream.setEncoding(DEFAULT_COMPRESS_ENCODE); 
            } else { 
                zipStream.setEncoding(encode); 
            } 
            return zipStream; 
        } 
     
        /**
         * 访问本地路径下的所有文件
         * 
         * @param path
         * @return
         */ 
        public static List<File> loadFiles(String path) { 
            List<File> list = null; 
            try { 
                File fold = new File(path); 
                if (fold.isDirectory()) { 
                    File[] files = fold.listFiles(); 
                    list = Arrays.asList(files); 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } 
            return list; 
        } 
     
        /**
         * 读取本地系统路径下的所有图片打成ZIP
         * 
         * @param path
         * @param output
         * @param compress
         */ 
        public static void compressZip(String path, OutputStream output, 
                String encode, boolean compress) { 
            List<File> listfiles = null; 
            ZipOutputStream zipStream = null; 
            try { 
                zipStream = getZipStreamEncode(output, encode); 
                listfiles = loadFiles(path); 
                for (File file : listfiles) { 
                    compressZip(file, zipStream, compress); 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (zipStream != null) { 
                        zipStream.close(); 
                    } 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
     
        /**
         * 读取网络图片打成打成ZIP
         * @param urls
         *            key = 图片名, value = 图片URL
         * @param output 
         * @param encode 编码
         * @param compress 是否压缩
         */ 
        public static void compressZip(Map<String, String> urls, 
                OutputStream output, String encode, boolean compress) { 
            ZipOutputStream zipStream = null; 
            try { 
                zipStream = getZipStreamEncode(output, encode); 
                Map<String, String> synUrls = Collections.synchronizedMap(urls); 
                Set<Entry<String, String>> set = synUrls.entrySet(); 
                Iterator<Entry<String, String>> it = set.iterator(); 
                while (it.hasNext()) { 
                    Entry<String, String> entry = it.next(); 
                    compressZip(entry.getValue(), zipStream, entry.getKey(), 
                            compress); 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (zipStream != null) { 
                        zipStream.close(); 
                    } 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
     
        /**
         * 压缩单个文件为ZIP
         * @param file
         * @param output
         * @param encode
         * @param compress
         */ 
        public static void compressZip(File file, OutputStream output, 
                String encode, boolean compress) throws Exception{ 
            FileInputStream input = null; 
            try { 
                input = new FileInputStream(file); 
                compressZip(input,file.getName(),output,encode,compress); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            }  finally { 
                try { 
                    if (input != null) 
                        input.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
             
        } 
     
        /**
         * 压缩单个文件为ZIP
         * @param input
         * @param fileName
         * @param output
         * @param encode
         * @param compress
         */ 
        public static void compressZip(InputStream input, String fileName, 
                OutputStream output, String encode, boolean compress) throws Exception { 
            ZipOutputStream zipStream = null; 
            try { 
                zipStream = getZipStreamEncode(output, encode); 
                zip(input, zipStream, fileName, compress); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (zipStream != null) 
                        zipStream.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
     
        /**
         * 本地图片
         */ 
        private static void compressZip(File file, ZipOutputStream zipStream, 
                boolean compress) throws Exception{ 
            FileInputStream input = null; 
            try { 
                input = new FileInputStream(file); 
                zip(input, zipStream, file.getName(), compress); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            }finally{ 
                try { 
                    if(input != null) 
                        input.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
     
        /**
         * 网络图片
         * 
         * @param url
         * @param zipStream
         * @param compress
         */ 
        private static void compressZip(String url, ZipOutputStream zipStream, 
                String fileName, boolean compress) throws Exception{ 
            InputStream input = null; 
            try { 
                input = HttpHelpers.getInputStream(url); 
                zip(input, zipStream, fileName, compress); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally{ 
                try { 
                    if(input != null) 
                        input.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
     
        /**
         * @param input
         * @param zipStream
         * @param zipEntryName
         * @param compress
         */ 
        private static void zip(InputStream input, ZipOutputStream zipStream, 
                String zipEntryName, boolean compress) throws Exception{ 
            byte[] bytes = null; 
            BufferedInputStream bufferStream = null; 
            try { 
                if(input == null) 
                    throw new Exception("获取压缩的数据项失败! 数据项名为:" + zipEntryName); 
                // 压缩条目不是具体独立的文件,而是压缩包文件列表中的列表项,称为条目,就像索引一样 
                ZipEntry zipEntry = new ZipEntry(zipEntryName); 
                // 定位到该压缩条目位置,开始写入文件到压缩包中 
                zipStream.putNextEntry(zipEntry); 
                if (compress) { 
                    bytes = CompressPictureTools.compressOfQuality(input, 0); 
                    zipStream.write(bytes, 0, bytes.length); 
                } else { 
                    bytes = new byte[1024 * 5];// 读写缓冲区 
                    bufferStream = new BufferedInputStream(input);// 输入缓冲流 
                    int read = 0; 
                    while ((read = bufferStream.read(bytes)) != -1) { 
                        zipStream.write(bytes, 0, read); 
                    } 
                } 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    if (null != bufferStream) 
                        bufferStream.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
    } 

 

分享到:
评论

相关推荐

    java后台批量下载文件并压缩成zip下载的方法

    Java后台批量下载文件并压缩成zip下载的方法 Java后台批量下载文件并压缩成zip下载的方法是指在Java后台中批量下载文件,并将其压缩成zip文件下载到本地桌面。该方法具有重要的参考价值,感兴趣的小伙伴们可以参考...

    java后端将html转换pdf.zip

    这通常需要额外的库,如Nashorn JavaScript引擎(Java 8及以下)或GraalVM(Java 9及以上)。 - 考虑到PDF的安全性和可访问性,可以设置PDF元数据、数字签名或添加书签。 5. **错误处理和性能优化**: - 在实际...

    Spring Boot中使用Java实现文件批量下载与压缩功能

    通过以上步骤,我们成功地实现了一个简单的Spring Boot服务,该服务能够在接收到特定请求时将多份文件打包成ZIP压缩文件并提供给用户一次性下载。这种方式不仅极大地提升了用户体验,还简化了服务器端的处理逻辑。...

    java动态导出excel压缩成zip下载的方法

    本文主要介绍了java动态导出excel压缩成zip下载的方法,该方法可以将excel文件压缩成zip格式并下载,具有实用价值。 java动态导出excel压缩成zip下载的原理 java动态导出excel压缩成zip下载的方法主要通过使用java...

    Java实现批量下载并压缩文件.pptx.pptx

    通过以上步骤,可以实现Java环境下从MinIO服务器批量下载文件并压缩为ZIP包的功能,这对于数据迁移、备份和分发场景非常有用。在实际开发中,还需要考虑错误处理、并发下载优化、资源管理等细节,以确保程序的稳定性...

    java实现批量下载 多文件打包成zip格式下载

    在Java编程中,批量下载和将多个文件打包成ZIP格式是一项常见的需求,特别是在处理大量数据或文件时。这里,我们看到一个示例展示了如何在Java中实现这一功能。首先,让我们详细了解一下涉及到的关键知识点。 1. **...

    JAVA 根据Url把多文件打包成ZIP下载实例

    JAVA 根据 URL 把多文件打包成 ZIP 下载实例是指通过 JAVA 将多个文件根据 URL 下载并打包成 ZIP 文件的过程。在这个过程中,我们可以使用 JAVA 的 URL 连接和 ZIP 压缩技术来实现文件的下载和打包。 在这个例子中...

    通过Java的Zip输入输出流实现压缩和解压文件

    对hutool的ZipUtil进行的封装,更加好用方便

    java多线程实现下载图片并压缩

    "java多线程实现下载图片并压缩" 在本文中,我们将详细介绍java多线程实现下载图片并压缩的过程,涵盖了 SpringMVC定时任务的实现、FTP环境搭建、图片下载和压缩等多个方面。 多线程实现下载图片 在多线程实现...

    Aspose.Words 转图片后打印模糊 解决思路.zip

    提供的"Aspose.Words 转图片后打印模糊 解决思路.docx"文档可能包含了具体实现的步骤和示例代码,建议详细阅读并结合实际项目进行调整。记住,每个项目都有其特殊性,可能需要根据具体情况调整参数和策略,以达到...

    JAVA-阿里云OSS文件下载并将文件压缩为ZIP格式保存-附件资源

    JAVA-阿里云OSS文件下载并将文件压缩为ZIP格式保存-附件资源

    java -minio.rar

    Java MinIO API 使用详解 MinIO是一款开源的对象存储服务,它兼容Amazon S3云存储接口,为开发者提供了高效、安全的数据存储解决方案。本教程将深入探讨如何使用Java SDK与MinIO进行交互,包括文件的上传、下载、...

    java源码包---java 源码 大量 实例

     这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行jar就可以了。 Java 3DMenu 界面源码 5个目标文件 ...

    Java多文件以ZIP压缩包导出的实现方法

    Java多文件以ZIP压缩包导出的实现方法主要围绕着Java编程语言对文件进行打包压缩的过程展开,涵盖了如何使用Java对服务器上的图片或其他文件进行压缩并导出为ZIP格式的压缩包。在实现这一功能的过程中,开发者需要对...

    java源码包3

     这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行jar就可以了。 Java 3DMenu 界面源码 5个目标文件 ...

    java源码包2

     这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行jar就可以了。 Java 3DMenu 界面源码 5个目标文件 ...

    JAVA上百实例源码以及开源项目源代码

    J2ME优化压缩PNG文件 4个目标文件 内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理 这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失...

    新建压缩(zipped)文件夹.zip

    ZIP格式允许我们将多个文件或文件夹打包成一个单一的压缩文件,以减少存储空间和方便传输。 【描述】中的链接"http://www.dcsdn.com/doc/d-7757"指向了一个可能的在线资源,通常这种链接会提供与压缩文件相关的文档...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    J2ME优化压缩PNG文件 4个目标文件 内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理 这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失...

Global site tag (gtag.js) - Google Analytics