20180314-程式碼分享

weixin_34148340發表於2018-03-14

1.單元測試簡化

/**
 * 下載單個材料(成功)
 * <p>
 * 用例1 jzId,clId均不為空
 *
 * @throws Exception Exception
 * @see ClController#downloadCl(HttpServletResponse, String, String, String, String, String, Integer)
 */
@Test
public void downloadCl_success() throws Exception {
    File file = File.createTempFile(UUIDUtils.getUuid(), ".jpg");
    InputStream is = new FileInputStream(file);
    Mockito.when(downloadTreeService.downloadTree(Mockito.anyString(), Mockito.any()
            , Mockito.anyString(), Mockito.anyInt())).thenReturn(is);
    Mockito.doNothing().when(czrzServiceImpl).addCzrz(any(CzrzEntity.class));
    this.mockMvc.perform(
            post("/rest/v4/cl/F995A1849D7F4BAC853964D8E0A5D958/actions/download?" +               "jzId=C7B5327BBBBD4DC6B607F412978D6939&jzMc=juanzong&clMc=yfj.docx&type=CAI_LIAO&downloadType=1"))
            .andExpect(status().isOk());
    IOUtils.closeQuietly(is);
    boolean delete = file.delete();
    if (!delete){
        throw new ResourceDeletedFailedException("臨時檔案刪除失敗, pash:" + file.getAbsolutePath());
    }
}

問題:

  1. is需要手動關閉,甚至中間拋錯流還關閉不了
  2. 臨時檔案需要手動刪除

修改後:

/**
 * 下載單個材料(成功)
 * <p>
 * 用例1 jzId,clId均不為空
 *
 * @throws Exception Exception
 * @see ClController#downloadCl(HttpServletResponse, String, String, String, String, String, Integer)
 */
@Test
public void downloadCl_success() throws Exception {
    File file = File.createTempFile(UUIDUtils.getUuid(), ".jpg");
    try (InputStream is = new TempFileInputStream(file.getAbsolutePath(), new FileInputStream(file))) {
        Mockito.when(downloadTreeService.downloadTree(Mockito.anyString(), Mockito.any()
                , Mockito.anyString(), Mockito.anyInt())).thenReturn(is);
        Mockito.doNothing().when(czrzServiceImpl).addCzrz(any(CzrzEntity.class));
        this.mockMvc.perform(
                post("/rest/v4/cl/F995A1849D7F4BAC853964D8E0A5D958/actions/download?" +                   "jzId=C7B5327BBBBD4DC6B607F412978D6939&jzMc=juanzong&clMc=yfj.docx&type=CAI_LIAO&downloadType=1"))
                .andExpect(status().isOk());
    }
}

再次升級

``` java
/**
 * 下載單個材料(成功)
 * <p>
 * 用例1 jzId,clId均不為空
 *
 * @throws Exception Exception
 * @see ClController#downloadCl(HttpServletResponse, String, String, String, String, String, Integer)
 */
@Test
public void downloadCl_success() throws Exception {
        File file= ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "rotate.jpg");
       @Cleanup InputStream is = new FileInputStream(file);
        Mockito.when(downloadTreeService.downloadTree(Mockito.anyString(), Mockito.any()
                , Mockito.anyString(), Mockito.anyInt())).thenReturn(is);
        Mockito.doNothing().when(czrzServiceImpl).addCzrz(any(CzrzEntity.class));
        this.mockMvc.perform(
                post("/rest/v4/cl/F995A1849D7F4BAC853964D8E0A5D958/actions/download?" +         "jzId=C7B5327BBBBD4DC6B607F412978D6939&jzMc=juanzong&clMc=yfj.docx&type=CAI_LIAO&downloadType=1"))
                .andExpect(status().isOk());
}

2.處理邏輯錯誤

Set<String> fmlIds = new HashSet<>();
for (JzmlEntity ml : jzmlList) {
    fmlIds.add(ml.getPid());
}
//將目錄集合中的二級目錄剔除,並將二級目錄按照fml分組
Map<String, List<JzmlEntity>> fmlId2SecondMlsMap = Maps.newHashMap();
Iterator<JzmlEntity> iterator = jzmlList.iterator();
while (iterator.hasNext()) {
    JzmlEntity ml = iterator.next();
    if (!fmlIds.contains(ml.getId())) {
        List<JzmlEntity> secondMl = fmlId2SecondMlsMap.computeIfAbsent(ml.getPid(), k -> new ArrayList<>());
        secondMl.add(ml);
        //剔除二級目錄
        iterator.remove();
    }
}

場景

此處要實現的效果,是通過程式碼將一級和二級目錄分開。程式碼完成後測試,一級目錄和二級目錄同時傳入時一切正常。但是,提測前只傳入一級目錄,也被剔除了。

5884335-98f54f552aebe1ae.png
程式碼分享-2.png

問題

if中的程式碼塊!fmlIds.contains(ml.getId())翻譯過來“是不屬於卷和一級目錄”,此處屬於邏輯混亂導致。


3.堆記憶體溢位

pdf檔案拼接,測試200+檔案發現堆記憶體溢位

@Slf4j
public class PdfCopyMerger {
    public static void main(String[] args)  {
        String pdfPath = "F:\\test.pdf";
        String pdfMergePath = "F:\\test-merge2.pdf";
        try(FileOutputStream fileOutputStream = new leOutputStream(pdfMergePath)){
            Document document = new Document();
            PdfCopy pdfCopy = new PdfCopy(document, fileOutputStream);
            document.open();
            int num = 500;
            for(int i=0; i< num; i++) {
                System.out.println(i);
                FileInputStream stream = new FileInputStream(pdfPath);
                PdfReader pdfReader = new PdfReader(stream);
                pdfCopy.addDocument(pdfReader);
                IOUtils.closeQuietly(stream);
            }
            document.close();
        } catch(FileNotFoundException e) {
            log.error("file not found", e);
        } catch (DocumentException e) {
            log.error("已存在一個writer繫結document,不允許多個", e);
        } catch (IOException e) {
            log.error("io讀寫異常", e);
        } catch(Exception e) {
        log.error("", e);
      }
    }
}

日誌

Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.Thread.getName(Thread.java:1135)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:677)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3236)
    at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
    at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
    at com.itextpdf.text.io.StreamUtil.inputStreamToArray(StreamUtil.java:73)
    at com.itextpdf.text.io.RandomAccessSourceFactory.createSource(RandomAccessSourceFactory.java:145)
    at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:411)
    at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:431)
    at com.thunisoft.dzjz.server.PdfCopyMerger.main(PdfCopyMerger.java:34)

問題定位

5884335-ed5b48d00073f4eb.png
程式碼分享-3.png

圖中框定的數量會是n(pdf個數)*m(pdf單頁的屬性),也就是說你合併的數量越多記憶體空間佔用越大

改後

public class PdfMerger {
    /**
     * 實現pdf拼接
     * @param args
     */
    public static void main(String[] args) throws IOException, DocumentException {
      long startTime = System.currentTimeMillis();
      String pdfPath = "C:\\Users\\liull\\Downloads\\test.pdf";
      String pdfMergePath = "C:\\Users\\liull\\Downloads\\test-merge2.pdf";
      int n = 1000;
        Document document = new Document();
        FileOutputStream fileOut = new FileOutputStream(pdfMergePath);
        PdfWriter pdfWriter = PdfWriter.getInstance(document, fileOut);
        document.open();
        PdfContentByte pdfContent = pdfWriter.getDirectContent();
      for(int i=0; i< n; i++) {
          System.out.println(i);
          InputStream in = new FileInputStream(pdfPath);
          PdfReader pdfReader = new PdfReader(in);
          int count = pdfReader.getNumberOfPages();
          for(int num = 0; num < count; ) {
              document.newPage();
              PdfImportedPage pdfImportedPage = pdfWriter.getImportedPage(pdfReader, ++num);
              pdfContent.addTemplate(pdfImportedPage, 0,0);
              pdfWriter.flush();
          }
          pdfWriter.freeReader(pdfReader);
          IOUtils.closeQuietly(in);
          pdfReader.close();
      }
        document.close();
        long end = System.currentTimeMillis();
        System.out.println(end-startTime);
    }
}

為啥用它

PDFWriter其實快取的只有每次的PdfReader所有pdfImportedPage(template),在pdfWriter.freeReader中將快取的內容重新整理至OutputStream中。

引用

第三個例子結論引用麗利姐內網發的帖子,求頂:
http://artery.thunisoft.com/form/5f31836588723b4adbe808c2d4434c1a/insert?id=ba962a412f3647f6b8cd6c7295ad7b85

相關文章