背景

因项目需求,项目中需要提供pdf压缩功能。将某一页压缩至1M大小。
场景的Java的pdf处理方案就是itext pdfbox 以及 apose

方案一:itext压缩(不推荐)

代码

 /*** @param src  源文件* @param dest 目标文件* @throws IOException* @throws DocumentException*/public static void compressPdf(String src, String dest, float factor)throws PdfCompressException {log.info("use radio {} compress file:{}>>>{}", factor, src, dest);// 读取pdf文件PdfReader reader = null;PdfStamper stamper = null;ByteArrayOutputStream imgBytes = null;try {reader = new PdfReader(src);int n = reader.getXrefSize();PdfObject object;PRStream stream;// Look for image and manipulate image streamfor (int i = 0; i < n; i++) {object = reader.getPdfObject(i);if (object == null || !object.isStream()) {continue;}stream = (PRStream) object;PdfObject pdfSubByte = stream.get(PdfName.SUBTYPE);if (pdfSubByte != null && pdfSubByte.toString().equals(PdfName.IMAGE.toString())) {PdfImageObject image = new PdfImageObject(stream);BufferedImage bi = image.getBufferedImage();if (bi == null) {continue;}int width = bi.getWidth();int height = bi.getHeight();AffineTransform at = AffineTransform.getScaleInstance(1, 1);if ((int) (width * factor) > 0 && (int) (bi.getHeight() * factor) > 0) {width = (int) (width * factor);height = (int) (bi.getHeight() * factor);at = AffineTransform.getScaleInstance(factor, factor);}BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics2D g = img.createGraphics();g.drawRenderedImage(bi, at);imgBytes = new ByteArrayOutputStream();ImageIO.write(img, "JPG", imgBytes);stream.clear();stream.setData(imgBytes.toByteArray(), false, PRStream.BEST_COMPRESSION);stream.put(PdfName.TYPE, PdfName.XOBJECT);stream.put(PdfName.SUBTYPE, PdfName.IMAGE);stream.put(PdfName.FILTER, PdfName.DCTDECODE);stream.put(PdfName.WIDTH, new PdfNumber(width));stream.put(PdfName.HEIGHT, new PdfNumber(height));stream.put(PdfName.BITSPERCOMPONENT, new PdfNumber(8));stream.put(PdfName.COLORSPACE, PdfName.DEVICERGB);}}stamper = new PdfStamper(reader, new FileOutputStream(dest));} catch (Exception e) {log.error("pdf compress error:{}>>>{}", src, dest);log.error("pdf compress error:", e);throw new PdfCompressException(e.getMessage());} finally {if (imgBytes != null) {try {imgBytes.close();} catch (IOException e) {log.error("imgBytes close failed when compress pdf:", e);}}if (stamper != null) {try {stamper.close();} catch (Exception e) {log.error("stamper close failed when compress pdf:", e);}}if (reader != null) {reader.close();}}}

方案描述

 提供一个压缩方法,先尝试 1倍压缩判断是否小于1M,然后0.9,0.8...0.1,直至factor<=0.1或者压缩后文件小于等于1M才停止压缩。

方案问题

该方案相当于至针对pdf当中的图片进行压缩,之前遇见一个10M的非图片pdf,就是一个表格,但是无论如何都压不下来。
后面采用wps和apose以及在线的pdf压缩工具同样处理不了。
压缩效率低,内存消耗巨大。

方案二:pdfbox方案(强烈不推荐)

代码

  public static void image2Pdf(String inputFile, String pdfFile) throws Image2PdfException {log.info("convert image 2 pdf :{}>>>{}", inputFile, pdfFile);Document doc = null;ByteArrayOutputStream outStream = null;PdfWriter pdfWriter = null;FileInputStream fi = null;try {File file = new File(inputFile);doc = new Document(PageSize.A4, 20, 20, 20, 20);pdfWriter = PdfWriter.getInstance(doc, new FileOutputStream(pdfFile));doc.open();doc.newPage();Image image;if (file.getName().toLowerCase().endsWith("jpg") || file.getName().toLowerCase().endsWith("jpeg")) {java.awt.Image awtImage = Toolkit.getDefaultToolkit().createImage(file.getAbsolutePath());image = Image.getInstance(awtImage, null);} else {image = Image.getInstance(file.getAbsolutePath());}float height = image.getHeight();float width = image.getWidth();if (width > height) {fi = new FileInputStream(file);BufferedImage src = ImageIO.read(fi);BufferedImage des1 = RotateImage.rotate(src, 90);String type = file.getName().substring(file.getName().lastIndexOf(".") + 1).toLowerCase();outStream = new ByteArrayOutputStream();ImageIO.write(des1, type, outStream);image = Image.getInstance(outStream.toByteArray());height = image.getHeight();width = image.getWidth();}int percent = getPercent(height, width);image.setAlignment(Image.MIDDLE);image.scalePercent(percent);float x = (PageSize.A4.getWidth() - image.getScaledWidth()) / 2;float y = (PageSize.A4.getHeight() - image.getScaledHeight()) / 2;image.setAbsolutePosition(x, y);doc.add(image);} catch (Exception e) {log.error("image 2 pdf failed:{}>>>{}", inputFile, pdfFile);log.error("exception info:", e);throw new Image2PdfException(e.getMessage());} finally {if (doc != null) {try {doc.close();} catch (Exception e) {log.info("空文档:", e);}}if (pdfWriter != null) {pdfWriter.close();}if (outStream != null) {try {outStream.close();} catch (IOException e) {e.printStackTrace();}}if (fi != null) {try {fi.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 等比压缩,获取压缩百分比** @param height 图片的高度* @param weight 图片的宽度* @return 压缩百分比*/private static int getPercent(float height, float weight) {float percent = 0.0F;if (height > weight) {percent = (PageSize.A4.getHeight() - 120) / height * 100;} else {percent = (PageSize.A4.getWidth() - 120) / weight * 100;}return Math.round(percent);}public static void pdf2ImagePdf(String source, String targetPdf, int dpi)throws Pdf2ImageException, PdfSplitException, Image2PdfException {String imagePath = source.substring(0, source.lastIndexOf("."));File imageDir = YhPdfUtil.pdf2Images(source, imagePath, dpi);File[] files = imageDir.listFiles();if (files == null || files.length == 0) {throw new Pdf2ImageException("no image found,may pdf 2 image failed");} else {if (files.length == 1) {log.info("pdf just one img ,just convert");YhPdfUtil.image2Pdf(files[0].getAbsolutePath(), targetPdf);} else {log.info("so much images,convert every img and merge all...");String tmpPdfDir =source.replace("\\", "/").substring(0, source.lastIndexOf(".")) + "-pdf-" + System.currentTimeMillis() + "/";File fpd = new File(tmpPdfDir);if (!fpd.exists()) {fpd.mkdirs();}for (int k = 0; k < files.length; k++) {String fn =files[k].getName().substring(0, files[k].getName().lastIndexOf(".")) + k + ".pdf";String tmpPdf = tmpPdfDir + fn;YhPdfUtil.image2Pdf(files[k].getAbsolutePath(), tmpPdf);}File[] tps = fpd.listFiles();if (tps == null || tps.length == 0) {throw new Image2PdfException("no pdf found,may image 2 pdf failed");} else {List<String> tst = new ArrayList<>();for (int l = 0; l < tps.length; l++) {tst.add(tps[l].getAbsolutePath());}tst.sort(Comparator.comparing(t -> t));YhPdfUtil.mergePdf(tst, targetPdf);try {FileUtils.deleteDirectory(imageDir);FileUtils.deleteDirectory(fpd);} catch (IOException e) {log.error("pdf转纯图pdf后,删除临时文件失败:", e);}}}}}public static void pdf2ImagePdfWithMax(String source, String targetPdf, long size)throws Pdf2ImageException, PdfSplitException, Image2PdfException, IOException {int dpi;File sourceFile = new File(source);if (sourceFile.length() <= size) {log.info("sourceFile's length:{}>size:{},just copy", sourceFile.length(), size);FileUtils.copyFile(sourceFile, new File(targetPdf));} else {long c = size * 1000 / sourceFile.length();c = c > 1000 ? 1000 : c;for (dpi = Integer.parseInt(String.valueOf(c)); dpi > 1; dpi = dpi / 2) {pdf2ImagePdf(source, targetPdf, dpi);File file = new File(targetPdf);if (file.length() > size) {continue;} else {break;}}}}/*** 合并pdf** @param fileList   本地文件列表 ["D:/opt/aaa.pdf","D:/opt/bbb.pdf"]* @param newPdfPath 合并文件的保存路径 "D:/opt/ccc.pdf"* @return boolean* @throws* @version V1.0.0* @date 2021/11/4 10:00*/public static boolean mergePdf(List<String> fileList, String newPdfPath) {Document document = null;FileOutputStream fo = null;PdfCopy copy = null;PdfReader rr = null;try {fo = new FileOutputStream(newPdfPath);rr = new PdfReader(fileList.get(0));document = new Document(rr.getPageSize(1));copy = new PdfCopy(document, fo);copy.setFullCompression();document.open();for (int i = 0; i < fileList.size(); i++) {PdfReader reader = new PdfReader(fileList.get(i));try {int n = reader.getNumberOfPages();for (int j = 1; j <= n; j++) {document.newPage();PdfImportedPage page = copy.getImportedPage(reader, j);copy.addPage(page);}} finally {reader.close();}}return true;} catch (IOException | DocumentException e) {log.error("pdf合并失败:", e);return false;} finally {if (rr != null) {rr.close();}if (copy != null) {copy.close();}if (document != null) {document.close();}if (fo != null) {try {fo.close();} catch (Exception e) {log.error("Io关闭异常:", e);}}}}

方案描述

该方案是通过pdfbox按某个dpi将pdf拆分成图片,然后在将拆出来的pdf通过itext合成为pdf.如果合并的pdf大于体积,则按更小的dpi再来一遍。

问题

其实该方案流程上没有问题,但是在性能上会存在非常大的漏洞及消耗-内存泄漏问题。pdfbox会缓存大量的pdf元数据(字体,字典)等信息
且无法被GC,或者说,在Gc之前,Java服务进程已经被服务器杀死了。刚开始还以为是版本问题,我看最新版本对内存做了优化,但是在升级
最新版本之后,内存增长虽然好了些,但是在有限的内存下。依旧无法会因内存泄漏问题导致服务宕机。

方案三:采用apose将pdf转为图片(不推荐)

代码

  public static File pdf2Images(String pdfPath, String imageDirPath, int dpi)throws Pdf2ImageException, PdfSplitException {imageDirPath = imageDirPath.replace("\\", "/");if (!imageDirPath.endsWith("/")) {imageDirPath = imageDirPath + "/";}File file = new File(pdfPath);File imageDir = new File(imageDirPath);if (!imageDir.exists()) {imageDir.mkdirs();}com.aspose.pdf.Document pdDocument;try {pdDocument = new com.aspose.pdf.Document(pdfPath);FileOutputStream fileOutputStream = null;int pages = pdDocument.getPages().size();if (pages == 1) {try {Resolution resolution = new Resolution(dpi);JpegDevice jpegDevice = new JpegDevice(resolution);String tmpImage = imageDirPath + file.getName().substring(0, file.getName().lastIndexOf(".")) +"-" + System.currentTimeMillis() + ".png";log.info("pdf just one page,use dpi {} pdf file 2 image:{}>>>{}", dpi, pdfPath, tmpImage);fileOutputStream = new FileOutputStream(new File(tmpImage));jpegDevice.process(pdDocument.getPages().get_Item(1), fileOutputStream);fileOutputStream.flush();} finally {pdDocument.close();if (fileOutputStream != null) {fileOutputStream.close();}}} else {log.info("the pdf so many pages, split every page before convert...");String tmpPdfPath =pdfPath.replace("\\", "/").substring(0, pdfPath.lastIndexOf(".")) + "-pdf-" + System.currentTimeMillis() + "/";File tmpPdfDir = splitPerPagePdf(pdfPath, tmpPdfPath);File[] files = tmpPdfDir.listFiles();if (files == null || files.length == 0) {throw new PdfSplitException("pdf split failed, no result fle found");} else {List<File> pdfs = new ArrayList<File>(Arrays.asList(files));pdfs.sort(Comparator.comparing(file1 -> file.getName()));for (int k = 0; k < pdfs.size(); k++) {pdf2Images(pdfs.get(k).getAbsolutePath(), imageDirPath, dpi);}FileUtils.deleteDirectory(new File(tmpPdfPath));}}return imageDir;} catch (IOException e) {log.error("pdf转图片失败:{}", e);throw new Pdf2ImageException(pdfPath);}}public static void image2Pdf(String inputFile, String pdfFile) throws Image2PdfException {log.info("convert image 2 pdf :{}>>>{}", inputFile, pdfFile);Document doc = null;ByteArrayOutputStream outStream = null;PdfWriter pdfWriter = null;FileInputStream fi = null;try {File file = new File(inputFile);doc = new Document(PageSize.A4, 20, 20, 20, 20);pdfWriter = PdfWriter.getInstance(doc, new FileOutputStream(pdfFile));doc.open();doc.newPage();Image image;if (file.getName().toLowerCase().endsWith("jpg") || file.getName().toLowerCase().endsWith("jpeg")) {java.awt.Image awtImage = Toolkit.getDefaultToolkit().createImage(file.getAbsolutePath());image = Image.getInstance(awtImage, null);} else {image = Image.getInstance(file.getAbsolutePath());}float height = image.getHeight();float width = image.getWidth();if (width > height) {fi = new FileInputStream(file);BufferedImage src = ImageIO.read(fi);BufferedImage des1 = RotateImage.rotate(src, 90);String type = file.getName().substring(file.getName().lastIndexOf(".") + 1).toLowerCase();outStream = new ByteArrayOutputStream();ImageIO.write(des1, type, outStream);image = Image.getInstance(outStream.toByteArray());height = image.getHeight();width = image.getWidth();}int percent = getPercent(height, width);image.setAlignment(Image.MIDDLE);image.scalePercent(percent);float x = (PageSize.A4.getWidth() - image.getScaledWidth()) / 2;float y = (PageSize.A4.getHeight() - image.getScaledHeight()) / 2;image.setAbsolutePosition(x, y);doc.add(image);} catch (Exception e) {log.error("image 2 pdf failed:{}>>>{}", inputFile, pdfFile);log.error("exception info:", e);throw new Image2PdfException(e.getMessage());} finally {if (doc != null) {try {doc.close();} catch (Exception e) {log.info("空文档:", e);}}if (pdfWriter != null) {pdfWriter.close();}if (outStream != null) {try {outStream.close();} catch (IOException e) {e.printStackTrace();}}if (fi != null) {try {fi.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 合并pdf** @param fileList   本地文件列表 ["D:/opt/aaa.pdf","D:/opt/bbb.pdf"]* @param newPdfPath 合并文件的保存路径 "D:/opt/ccc.pdf"* @return boolean* @throws* @version V1.0.0* @date 2021/11/4 10:00*/public static boolean mergePdf(List<String> fileList, String newPdfPath) {Document document = null;FileOutputStream fo = null;PdfCopy copy = null;PdfReader rr = null;try {fo = new FileOutputStream(newPdfPath);rr = new PdfReader(fileList.get(0));document = new Document(rr.getPageSize(1));copy = new PdfCopy(document, fo);copy.setFullCompression();document.open();for (int i = 0; i < fileList.size(); i++) {PdfReader reader = new PdfReader(fileList.get(i));try {int n = reader.getNumberOfPages();for (int j = 1; j <= n; j++) {document.newPage();PdfImportedPage page = copy.getImportedPage(reader, j);copy.addPage(page);}} finally {reader.close();}}return true;} catch (IOException | DocumentException e) {log.error("pdf合并失败:", e);return false;} finally {if (rr != null) {rr.close();}if (copy != null) {copy.close();}if (document != null) {document.close();}if (fo != null) {try {fo.close();} catch (Exception e) {log.error("Io关闭异常:", e);}}}}public static void compress(String source, String target,int qa) {new com.aspose.pdf.Document doc = new new com.aspose.pdf.Document(source);//设置压缩属性OptimizationOptions opt = new OptimizationOptions();//删除PDF不必要的对象opt.setRemoveUnusedObjects(true);//链接重复流opt.setLinkDuplcateStreams(false);//删除未使用的流opt.setRemoveUnusedStreams(false);//删除不必要的字体opt.setUnembedFonts(true);//压缩PDF中的图片opt.setCompressImages(true);//图片压缩比, 0 到100可选,越低压缩比越大opt.setImageQuality(qa);doc.optimizeResources(opt);//优化web的PDF文档doc.optimize();doc.save(target);}

方案描述

流程是 pdf转图片->图片转pdf->合并->循环压缩至指定大小,该方案解决了pdfbox内存泄漏问题

问题

虽然解决的pdfbox内存泄漏问题,但是内存占用依旧非常严重。几个文件转换,内存飙升4个G。对服务而言,
还是比较危险的,在内存宽裕的情况下,采用这套方案可以,但是在内存禁止的情况下,不建议如此去做。

方案四:ghostscript+ImageMagick(推荐,最终方案)

代码:

private static String command = "";private static final String cmdExpress = "%s -density 150 -quality %s -limit memory 10mb -limit map 10mb %s %s";private static String gsCommand = "";static {String os = System.getProperty("os.name");if (os != null && os.toLowerCase().contains("window")) {command = "magick";gsCommand = "gswin32c";} else if (os != null && os.toLowerCase().contains("ubuntu")) {command = "sudo convert";gsCommand = "sudo gs";} else {command = "convert";gsCommand = "gs";}}public static void pdf2ImagePdf(String pdfPath, String targetPdf, int qa)throws Pdf2ImageException, Image2PdfException {String imageDirPath = pdfPath.substring(0, pdfPath.lastIndexOf(".")).replace("\\", "/");log.info("pdf2image:{}>>>{}", pdfPath, imageDirPath);pdfPath = pdfPath.replace("\\", "/");File pdf = new File(pdfPath);String pdfName = pdf.getName();File imageDir = new File(imageDirPath);if (!imageDir.exists()) {imageDir.mkdirs();}String imageName = pdfName.substring(0, pdfName.lastIndexOf(".")) + ".png";String imageFilePath = imageDirPath + "/" + imageName;imageFilePath = imageFilePath.replace("\\", "/");String pdf2ImgCmd = String.format(cmdExpress, command, qa, pdfPath, imageFilePath);log.info("pdf2ImgCmd:{}", pdf2ImgCmd);try {Process pro = Runtime.getRuntime().exec(pdf2ImgCmd);pro.waitFor(5, TimeUnit.MINUTES);} catch (Exception e) {log.error("pdf转图片你失败:", e);throw new Pdf2ImageException(e.getMessage());}String inputFile = imageDirPath + "/*.png";String cmdEx = "%s  -density 150 -quality %s -limit memory 10mb -limit map 10mb %s %s";String img2PdfCmd = String.format(cmdEx, command, qa, inputFile, targetPdf);log.info("convert2PdfCmd:{}", img2PdfCmd);try {Process pro = Runtime.getRuntime().exec(img2PdfCmd);pro.waitFor(3, TimeUnit.MINUTES);} catch (Exception e) {log.error("pdf转图片你失败:", e);throw new Image2PdfException(e.getMessage());}FileUtil.del(imageDirPath);}/*** @param src  源文件* @param dest 目标文件* @throws IOException* @throws DocumentException*/public static void compressPdf(String src, String dest, int qa) throws IOException {String compressCommand = "%s -dQUIET -dNOSAFER -r%s -sDEVICE=pdfwrite -dCompatibilityLevel=1.3 -dPDFSETTINGS=/screen  -dNOPAUSE -dBATCH -dColorImageResolution=150 -sOutputFile=%s %s";src = src.replace("\\", "/");dest = dest.replace("\\", "/");String cmd = String.format(compressCommand, gsCommand, qa, dest, src);log.info(cmd);try {Process process = Runtime.getRuntime().exec(cmd);process.waitFor(3, TimeUnit.MINUTES);} catch (Exception e) {log.info("文档转换失败:", e);throw new PdfCompressException(e.getMessage());}}public static void pdf2ImagePdfWithMax(String source, String targetPdf, long size)throws IOException {File sourceFile = new File(source);if (sourceFile.length() <= size) {log.info("sourceFile's length:{}>size:{},just copy", sourceFile.length(), size);FileUtils.copyFile(sourceFile, new File(targetPdf));} else {String targetTmpPdf = targetPdf.substring(0, targetPdf.lastIndexOf(".")) + "-tmp" + ".pdf";try {FutureTask<Boolean> futureTask = new FutureTask<>(() -> {pdf2ImagePdf(source, targetTmpPdf, 96);compressPdf2FixLength(targetTmpPdf, targetPdf, size);return true;});YhConstant.ITEM_POOL.submit(futureTask);try {futureTask.get(5, TimeUnit.MINUTES);} catch (Exception e) {throw new PdfCompressException("压缩失败:" + e.getMessage());}} finally {File file = new File(targetTmpPdf);if (file.exists()) {file.delete();}}}}

方案描述:

流程依旧是 pdf转图片->图片合并成pdf->pdf压缩
只是通过系统层ghostscript+ImageMagick来实现
cenos:yum install -y ghostscript ImageMagickvi /etc/ImageMagick-6/policy.xml将 <policy domain="module"这一行取消注释,并改为:<policy domain="module" rights="read|write" pattern="{PS,PDF,XPS}" />
unbuntu:apt install -y ghostscript ImageMagick同样需要修改etc/magick安装目录下的policy.xml文件
windows:自行安装且添加环境变量。

问题

ghostscript压缩pdf稍微费些内存,但是比起java要好好多。建议在ghostscript压缩加入线程池进行并发控制,降低内存爆掉的风险。

总结

Java就是TMD费内存,JVM优化其实也就那样,5家客户同事在用的saas系统,我只能xms xmx服务器剩余的4个G,再怎么优化也是醉了。

Java-pdf无限压缩方案-优化内存问题相关推荐

  1. java pdf文件压缩_PDF文件压缩转换教程

    现在在很多的网站上传文件的时候,不知道大家有没有遇到因为pdf文件太大,不能上传这种情况.还有很多限制PDF文件大小,由于PDF文件较多,那么就会导致效率大大减小.如果PDF文件过大进行PDF文件传输 ...

  2. PDF文件压缩和优化的原理是什么?看了这篇C#案例实践就知道了

    这篇是我初步选择的几个可优化压缩的截图 一.类库的引入 using Pdftools.Pdf; using Pdftools.PdfOptimize; // DLL文件这里下载 https://www ...

  3. java 指针 地址压缩_JVM优化之压缩普通对象指针(CompressedOops)

    通常64位JVM消耗的内存会比32位的大1.5倍,这是因为对象指针在64位架构下,长度会翻倍(更宽的寻址). 对于那些将要从32位平台移植到64位的应用来说,平白无辜多了1/2的内存占用,这是开发者不 ...

  4. Linux内存从0到1学习笔记(11.2 内存优化方案之内存压缩zram)

    写在前面 zram是Linux内核提供的一种虚拟内存压缩功能,通过在将一部分内存模拟成块设备,并将压缩后的内存写到这部分模拟的块设备中,直到必须使用硬盘上的交换空间,zram 本质是就是一个块设备. ...

  5. Unity加载优化-将基于LZMA的ab压缩方案修改为LZ4压缩的过程

    # 观前提示 本文适合需要了解LZMA以及LZ4进行ab打包方案同学,以及会将一些资源管理的方案.如果恰好你也用xlua-framework,那就更适合你了. # 优化起因 最近新游戏在Iphone6 ...

  6. java实现对pdf文件压缩,拆分,修改水印,添加水印

    最近要实现一个文件上传,并且在线预览上传文件的功能,设计思路是:把上传的文件通过openoffice转成pdf文件,并将pdf文件以流的形式返回到浏览器,由于上传的部分文件过大,转成pdf后传回前端浏 ...

  7. 【在线网课】Java高性能高并发秒杀系统方案优化实战

    java教程视频讲座简介: Java高性能高并发秒杀系统方案优化实战 Java秒杀系统方案优化 高性能高并发实战 以"秒杀"这一Java高性能高并发的试金石场景为例,带你通过一系列 ...

  8. 如何批量对 PDF 文档进行优化与高效的压缩?

    概要:「我的ABC软件工具箱」提供了强大的批量 PDF 优化与压缩的功能,能够对已有的 PDF 文档进行优化与压缩,可以在一定程度上减少 PDF 文档占用空间的大小.整个操作过程非常简单,处理高效! ...

  9. 使用java iTest实现PDF大文件压缩——将文件过大的图片PDF文件压缩成小一些的图片PDF文件

    一.需求 项目中需要将文件大小过大的PDF文件,压缩成小PDF文件.通过iText的API,可以实现此需求.在保证文件不失真的前提下,将PDF大文件压缩成小文件. 二.代码 import com.it ...

最新文章

  1. 基于小型GIS的配电设备运行监控系统
  2. @Scheduled(cron=““) spring定时任务时间设置
  3. 清除距今天7天的日志
  4. Android——四大组件、六大布局、五大存储
  5. 服务器 Font family [‘sans-serif‘] not found.Falling back to DejaVu Sans.解决办法
  6. ECMAScript 2016,2017,和2018中新增功能
  7. java jsp总结
  8. 服务器支持磁盘阵列,服务器磁盘阵列、RAID级别的阐述
  9. 倒N字形排列java_Java排序8大算法实现
  10. MetadataCache分析
  11. android 玩pc游戏,Shield掌机试玩: Android系统 可玩PC单机游戏
  12. 广西科技大学计算机考研,广西科技大学研究生院
  13. Codeforces 36B - Fractal
  14. WiFi 空口抓包工具 --- OmniPeek
  15. 微信小程序开发进阶篇(mpvue)
  16. IT项目经理的基本条件
  17. 蒙德里安森林算法_蒙德里安的格子画教案
  18. word2016开机后首次打开非常慢_终于找到了电脑开机时间长的原因了,一看就会,一招到位...
  19. 灰度共生矩阵原理+Matlab中实现
  20. 推荐几种占位图生成工具,包含dummyimage、placehold和placekitten等

热门文章

  1. 2783: 魔法药水【二分】
  2. Shader学习2——兰伯特
  3. 三星Galaxy S5手机在全球125个国家同步上市
  4. 【知识产权基础之专利权】第一章 专利权的客体
  5. 外卖系统外卖O2O系统开发功能与开发难点介绍
  6. Mac M1配置Apache Tomcat
  7. OPC:客户端开发——应用WTclient.dll使用手册部分中文版
  8. ActionBarTest、FragmentTest
  9. 蒲公英R300A 4G路由器,远程监控PLC教程
  10. Fabric 1.0源代码分析(31) Peer