【问题标题】:Facing error java.io.IOException: Stream Closed while merging 2 pdfs面临错误 java.io.IOException: Stream Closed while merging 2 pdfs
【发布时间】:2018-06-29 08:09:26
【问题描述】:

我试图通过文件上传合并来自 JSP 页面的两个 PDF 文档。我正在获取文件,将它们放在一个列表中,但是当我尝试读取它们以进行合并时,我收到一条“Stream Closed”消息。我搜索了很多关于这个例外的回复,但没有任何帮助。所以,再放一次,不确定它在哪里失败。

下面是Java代码:

public class MergePdfUtility {
FormDataBean formData = null;

public String mergePdf(String contentType, InputStream inputStream, int formDataLength,
        HttpServletRequest request) {
    String saveFile = new String();
    String strUploadFile = "";
    File flUploadFile = null;
    formData = new FormDataBean();


    try {
        List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
        List<InputStream> list = new ArrayList<InputStream>();

        for (FileItem item : items) {
            if (item.isFormField()) {
                String fieldname = item.getFieldName();
                String fieldvalue = item.getString();
                if(fieldvalue == null || fieldvalue.length() == 0) {
                    fieldvalue = "Merger.pdf";
                }
                System.out.println("Field Value = " + fieldvalue);

                if (fieldname.equals("txtFileName")) {
                    formData.setStrFileName(fieldvalue);
                    System.out.println("successfully assigned");
                }
            } else {
                saveFile = FilenameUtils.getName(item.getName());                   
                if (saveFile.endsWith(".pdf")) {
                    inputStream = item.getInputStream();
                    contentType = item.getContentType();
                    strUploadFile = "D:\\Samik\\BCP\\" + saveFile;
                    flUploadFile = new File(strUploadFile);
                    FileOutputStream fileout = new FileOutputStream(flUploadFile);
                    BufferedOutputStream bout = new BufferedOutputStream(fileout);
                    BufferedInputStream bin = new BufferedInputStream(inputStream);
                    int byte_;

                    while ((byte_ = bin.read()) != -1) {
                        bout.write(byte_);
                    }
                    list.add(inputStream);

                    bout.close();
                    bin.close();
                    fileout.flush();
                    fileout.close();                        
                }
            }
        }

        mergePdf(list);
    } catch (Exception e) {         
        e.printStackTrace();            
        return "failure";
    }       
    return "success";
}

private static void mergePdf(List<InputStream> list)
        throws DocumentException, IOException {
    OutputStream mergeOutputStream = new FileOutputStream(new File("D:\\Samik\\BCP\\" + "Merger.pdf"));
    Document document = new Document();
    PdfWriter pdfWriter = PdfWriter.getInstance(document, mergeOutputStream);
    document.open();
    PdfContentByte pdfContentByte = pdfWriter.getDirectContent();
    for (InputStream tempInputStream : list) {
        PdfReader pdfReader = new PdfReader(tempInputStream);
        for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
            document.newPage();
            PdfImportedPage page = pdfWriter.getImportedPage(pdfReader, i);
            pdfContentByte.addTemplate(page, 0, 0);
        }
    }
    mergeOutputStream.close();
    document.close();
}

}

我遇到了异常:

PdfReader pdfReader = new PdfReader(tempInputStream);

例外:

java.io.IOException: Stream Closed
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(Unknown Source)
at com.itextpdf.text.pdf.RandomAccessFileOrArray.InputStreamToArray(RandomAccessFileOrArray.java:179)
at com.itextpdf.text.pdf.RandomAccessFileOrArray.<init>(RandomAccessFileOrArray.java:172)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:237)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:248)
at com.tcs.pdfutil.MergePdfUtility.mergePdf(MergePdfUtility.java:108)
at com.tcs.pdfutil.MergePdfUtility.mergePdf(MergePdfUtility.java:86)
at org.apache.jsp.MergePdfUtility_jsp._jspService(MergePdfUtility_jsp.java:83)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at   org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

具体的问题是什么? inputStream 在哪里关闭?

================================================ ================================ 7 月 3 日添加:

感谢@Anonymity。这帮助我解决了这个问题,但只是部分解决了。我现在最多可以合并 5 个 pdf 文档,甚至可以在合并后删除源文档 - 但我仍然无法收到成功消息。在完美地完成所有操作之后,控件转到 IOException,我最后得到了同样的错误 - java.io.IOException: Stream Closed,这导致返回“失败”消息。无法追踪我哪里出错了。

我的代码现在看起来像这样:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;

public class MergePdfUtility {
FormDataBean formData = null;

public String mergePdf(String contentType, int formDataLength, HttpServletRequest request) {
    String saveFile = new String();
    String strUploadFile = "";
    File flUploadFile = null;
    formData = new FormDataBean();
    FileOutputStream fileout = null;
    BufferedOutputStream bout = null;
    BufferedInputStream bin = null;
    String strStatusMsg;
    InputStream inputStream1 = null;
    InputStream inputStream2 = null;
    InputStream inputStream3 = null;
    InputStream inputStream4 = null;
    InputStream inputStream5 = null;

    try {
        List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
        List<InputStream> list = new ArrayList<InputStream>();
        //List<InputStream> finalList = new ArrayList<InputStream>();

        for (FileItem item : items) {
            if (item.isFormField()) {
                String fieldname = item.getFieldName();
                String fieldvalue = item.getString() + ".pdf";
                if (fieldvalue == null || fieldvalue.length() == 0 || fieldvalue.equals(".pdf")) {
                    fieldvalue = "Merger.pdf";
                }
                System.out.println("Field Value = " + fieldvalue);

                if (fieldname.equals("txtFileName")) {
                    formData.setStrFileName(fieldvalue);
                    System.out.println("successfully assigned");
                }
            } else {
                saveFile = FilenameUtils.getName(item.getName());
                if (saveFile.endsWith(".pdf")) {
                    String strFieldName = item.getFieldName();
                    System.out.println(saveFile);

                    contentType = item.getContentType();
                    strUploadFile = "D:\\Samik\\BCP\\" + saveFile;
                    flUploadFile = new File(strUploadFile);
                    fileout = new FileOutputStream(flUploadFile);
                    bin = new BufferedInputStream(item.getInputStream());
                    bout = new BufferedOutputStream(fileout);

                    int byte_;

                    while ((byte_ = bin.read()) != -1) {
                        bout.write(byte_);
                    }
                    bout.flush();
                    bout.close();
                    //bin.close();
                    if(strFieldName.endsWith("1")) {
                        inputStream1 = new FileInputStream(flUploadFile);
                        list.add(inputStream1);
                        formData.setFile1(flUploadFile);
                    } else if(strFieldName.endsWith("2")) {
                        inputStream2 = new FileInputStream(flUploadFile);
                        list.add(inputStream2);
                        formData.setFile2(flUploadFile);
                    }
                    else if(strFieldName.endsWith("3")) {
                        inputStream3 = new FileInputStream(flUploadFile);
                        list.add(inputStream3);
                        formData.setFile3(flUploadFile);
                    }
                    else if(strFieldName.endsWith("4")) {
                        inputStream4 = new FileInputStream(flUploadFile);
                        list.add(inputStream4);
                        formData.setFile4(flUploadFile);
                    }
                    else if(strFieldName.endsWith("5")) {
                        inputStream5 = new FileInputStream(flUploadFile);
                        list.add(inputStream5);
                        formData.setFile5(flUploadFile);
                    }
                }
            }
        }

        mergePdf(list, formData);
        System.out.println("Finishing the main method");
        if(flUploadFile != null) {
            flUploadFile.delete();
        }
        strStatusMsg = "success";
    } catch (Exception e) {
        e.printStackTrace();
        strStatusMsg = "failure";
    }
    return strStatusMsg;
}

private static void mergePdf(List<InputStream> list, FormDataBean formData) {
    OutputStream mergeOutputStream = null;
    Document document = null;
    try {
        mergeOutputStream = new FileOutputStream(new File("D:\\Samik\\BCP\\" + formData.getStrFileName()));
        document = new Document();
        PdfWriter pdfWriter = PdfWriter.getInstance(document, mergeOutputStream);
        document.open();
        PdfContentByte pdfContentByte = pdfWriter.getDirectContent();
        for (InputStream tempInputStream : list) {
            PdfReader pdfReader = new PdfReader(tempInputStream);
            for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
                document.newPage();
                PdfImportedPage page = pdfWriter.getImportedPage(pdfReader, i);
                pdfContentByte.addTemplate(page, 0, 0);                 
            }

            tempInputStream.close();
            pdfReader.close();
        }
        System.out.println("Merging completed");
        deleteFiles(formData);
        pdfWriter.flush();
        pdfWriter.close();
    } catch (DocumentException | IOException e) {
        try {
            throw e;
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    } finally {
        try {
            if (mergeOutputStream != null) {
                mergeOutputStream.close();
            }
            if (document != null) {
                document.close();
            }
            System.out.println("Exiting the method");
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}

private static void deleteFiles(FormDataBean formData) throws IOException, FileNotFoundException {
    if(formData.getFile1()!=null) {
        formData.getFile1().delete();
    }
    if(formData.getFile2()!=null) {
        formData.getFile2().delete();
    }
    if(formData.getFile3()!=null) {
        formData.getFile3().delete();
    }
    if(formData.getFile4()!=null) {
        formData.getFile4().delete();
    }
    if(formData.getFile5()!=null) {
        formData.getFile5().delete();
    }
}
}

【问题讨论】:

  • 您能否分享IOException 的堆栈跟踪以获取您更改的代码?
  • ExceptionConverter: java.io.IOException: Stream Closed at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(Unknown Source) at java.io.BufferedOutputStream.flushBuffer (未知来源)在 java.io.BufferedOutputStream.flush(未知来源)在 com.itextpdf.text.pdf.OutputStreamCounter.flush(OutputStreamCounter.java:89) 在 com.itextpdf.text.DocWriter.close(DocWriter.java: 233) 在 com.itextpdf.text.pdf.PdfWriter.close(PdfWriter.java:1281) 在 com.tcs.pdfutil.MergePdfUtility.mergePdf(MergePdfUtility.java:150)
  • 啊,正如您在其中看到的,新的IOException 被置于完全不同的环境中。
  • 但是您确定您发布的代码发生了异常堆栈跟踪吗?您在MergePdfUtility.mergePdf 中显式调用PdfWriter.close 的唯一时间是在pdfWriter.flush() 之后,并且flush 调用似乎没有导致异常。因此,没有明显的原因导致基础流已经在 PdfWriter.close 中关闭......当然@Anonymity 是正确的,您应该使用 PdfCopy 系列中的类来合并 PDF。

标签: java pdf


【解决方案1】:

据我了解,当BufferedInputStream 如下关闭时,InputStream 在传递到mergePdf() 之前已经关闭。

BufferedInputStream bin = new BufferedInputStream(inputStream);
...
list.add(inputStream);
...
bin.close();

您可以查看java.io.BufferedInputStream#close的代码

public void close() throws IOException {
    byte[] buffer;
    while ( (buffer = buf) != null) {
        if (bufUpdater.compareAndSet(this, buffer, null)) {
            InputStream input = in;
            in = null;
            if (input != null)
                input.close();
            return;
        }
        // Else retry in case a new buf was CASed in fill()
    }
}

PS:我建议你关闭 finally 块中的 IO 流或使用 try-with-resources。例如:

private static void mergePdf(List<InputStream> list) {
    OutputStream mergeOutputStream = null;
    Document document = null;
    try{
        mergeOutputStream = new FileOutputStream(new File("D:\\Samik\\BCP\\" + "Merger.pdf"));
        document = new Document();
        PdfWriter pdfWriter = PdfWriter.getInstance(document, mergeOutputStream);
        document.open();
        PdfContentByte pdfContentByte = pdfWriter.getDirectContent();
        for (InputStream tempInputStream : list) {
            PdfReader pdfReader = new PdfReader(tempInputStream);
            for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
                document.newPage();
                PdfImportedPage page = pdfWriter.getImportedPage(pdfReader, i);
                pdfContentByte.addTemplate(page, 0, 0);
            }
        }
    } catch (DocumentException|IOException e) {
        throw e;
    } finally {
        try {
            if(mergeOutputStream != null) {
                mergeOutputStream.close();
            }
            if(document != null) {
                document.close();
            }
        } catch (IOException e1) {
            e1.printStackTrace(); 
        }
    }
}

或(确保 Document 已实现 java.io.Closeable):

private static void mergePdf(List<InputStream> list) throws DocumentException, IOException {
    try(OutputStream mergeOutputStream = new FileOutputStream(new File("D:\\Samik\\BCP\\" + "Merger.pdf")); Document document = new Document()){
        PdfWriter pdfWriter = PdfWriter.getInstance(document, mergeOutputStream);
        document.open();
        PdfContentByte pdfContentByte = pdfWriter.getDirectContent();
        for (InputStream tempInputStream : list) {
            PdfReader pdfReader = new PdfReader(tempInputStream);
            for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
                document.newPage();
                PdfImportedPage page = pdfWriter.getImportedPage(pdfReader, i);
                pdfContentByte.addTemplate(page, 0, 0);
            }
        }
    }
}

2018 年 7 月 4 日更新。根据https://developers.itextpdf.com/question/how-merge-documents-correctly 的测试方法。

public static void mergePdf(List<InputStream> inputStreams, String destinationFile) {
    try (OutputStream mergeOutputStream = new FileOutputStream(new File(destinationFile))) {
        Document document = new Document();
        PdfCopy copy = new PdfSmartCopy(document, mergeOutputStream);
        document.open();
        for (InputStream tempInputStream : inputStreams) {
            PdfReader pdfReader = new PdfReader(tempInputStream);
            copy.addDocument(pdfReader);
            System.out.println("Merging completed");
            pdfReader.close();
            tempInputStream.close();
        }
        document.close();
        copy.close();
    } catch (DocumentException | IOException e) {
        e.printStackTrace();
    }
}

于 2018 年 7 月 5 日更新 不知道你用的是什么框架。但是我已经成功地将两个 pdf 与 Springboot 2.0.1.RELEASE 合并。这是我的测试代码

@ResponseBody
@RequestMapping(value = "/mergePdf.json", method = POST)
public void mergePdf(@RequestParam("files") MultipartFile[] files) {
    mergePdfViaIText(Arrays.stream(files).map(file -> {
        try {
            LOG.debug(file.getOriginalFilename());
            return file.getInputStream();
        } catch (IOException e) {
            return null;
        }
    }).filter(Objects::nonNull).collect(Collectors.toList()), "D:/result.pdf");
}

public static void mergePdfViaIText(List<InputStream> inputStreams, String destinationPath) {
    try (OutputStream mergeOutputStream = new FileOutputStream(new File(destinationPath))) {
        Document document = new Document();
        PdfCopy copy = new PdfSmartCopy(document, mergeOutputStream);
        document.open();
        for (InputStream tempInputStream : inputStreams) {
            PdfReader pdfReader = new PdfReader(tempInputStream);
            copy.addDocument(pdfReader);
            LOG.info("Merging completed");
            pdfReader.close();
            tempInputStream.close();
        }
        document.close();
        copy.close();
    } catch (DocumentException | IOException e) {
        LOG.error(e.getMessage(), e);
    }
}

【讨论】:

  • 感谢@Anonymity 的解释。它对我帮助很大 - 工作已经完成,但我仍然面临同样的问题,虽然部分。修改后的代码在上面的主帖中更新。你能再帮我理解一下这个问题吗?
  • @user3358385 首先,如果需要,请始终关闭 finally 块中的 IO 流。如我所见,您正在从 http 请求解析文件。如果item.isFormField() 不正确,您会将 5 个文件合并为 1 个文件并删除这 5 个文件。我认为合并前没有必要保存它们,因为它们最终会被删除。
  • @user3358385 哦,我的错。很抱歉在我上次的评论中误导了你。这与关闭 IO 流的方式无关。您没有使用正确的方法来合并 pdf。 PdfSmartCopy 应该比PdfWriter 更适合这里。请参考developers.itextpdf.com/question/how-merge-documents-correctly 或者您可以查看我发布的测试方法。
  • 我确定我错过了一些东西,但仍然不确定我到底错过了什么。我检查了你的链接,从那里我去了 itextpdf 示例页面。那里列出了很多示例,除了合并两个 PDF 的示例 - 就这么简单。我使用 PdfCopy 和 PdfSmartCopy 而不是 PdfWriter,但我遇到了同样的问题。
  • 要求是,有一个UI界面,用户最多可以上传5个PDF文件,当用户点击一个按钮时,会合并这5个(2个必须合并,其余3个可选) 单个文件中的 PDF。当我用它们的位置硬编码文件名时,它们工作得很好,但当我动态选择它们时就不行了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-11
  • 2019-01-12
  • 2023-03-25
  • 2014-05-18
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
相关资源
最近更新 更多