传递pdf文件的输入流时管道损坏

Posted

技术标签:

【中文标题】传递pdf文件的输入流时管道损坏【英文标题】:broken pipe when passing input stream of pdf file 【发布时间】:2018-06-05 07:06:23 【问题描述】:

我正在创建一个应用程序,它使用 FTP/CLoudRail 通过 Internet 访问文件。在打开文件时,我使用这种方法首先获取输入流,然后将其解析为 ParcelFileDescriptor。问题是,当打开 PDF 文件时,我得到了 brokenPipe 错误。请注意,图像/文本文件等其他文件类型不会发生这种情况

这是我使用的代码:

@Override
public ParcelFileDescriptor openDocument(final String documentId, final String mode,
                                         CancellationSignal signal)
        throws FileNotFoundException 
    Log.d(TAG,"Opening document");
    final CloudFile file = getFileForDocId(documentId);
    final CloudConnection connection = getCloudConnection(documentId);
    //TODO Open a file
    try 
        final boolean isWrite = (mode.indexOf('w') != -1);
        if (isWrite) 
            return null;
         else 
            InputStream inputStream = connection.getConnectedClient().download(file.getPath());
            if(null != inputStream)
                return ParcelFileDescriptorUtil.pipeFrom(inputStream);
            
        

        return null;
     catch (Exception e) 
        CrashReportingManager.logException(e);
        throw new FileNotFoundException("Failed to open document with id " + documentId +
                " and mode " + mode);
    

这里是 ParcelFileDescriptorUtil:

public class ParcelFileDescriptorUtil 
public static ParcelFileDescriptor pipeFrom(InputStream inputStream)
        throws IOException 
    final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
    final OutputStream output = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]);

    new TransferThread(inputStream, output).start();

    return pipe[0];


@SuppressWarnings("unused")
public static ParcelFileDescriptor pipeTo(OutputStream outputStream)
        throws IOException 
    final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
    final InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0]);

    new TransferThread(input, outputStream).start();

    return pipe[1];


static class TransferThread extends Thread 
    final InputStream mIn;
    final OutputStream mOut;

    TransferThread(InputStream in, OutputStream out) 
        super("ParcelFileDescriptor Transfer Thread");
        mIn = in;
        mOut = out;
        setDaemon(true);
    

    @Override
    public void run() 
        try 
            IoUtils.copy(mIn, mOut);
         catch (IOException e) 
            Log.e("TransferThread", "writing failed");
            CrashReportingManager.logException(e);
         finally 
            IoUtils.flushQuietly(mOut);
            IoUtils.closeQuietly(mIn);
            IoUtils.closeQuietly(mOut);
        
    


public static int parseMode(String mode) 
    final int modeBits;
    if ("r".equals(mode)) 
        modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
     else if ("w".equals(mode) || "wt".equals(mode)) 
        modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
                | ParcelFileDescriptor.MODE_CREATE
                | ParcelFileDescriptor.MODE_TRUNCATE;
     else if ("wa".equals(mode)) 
        modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
                | ParcelFileDescriptor.MODE_CREATE
                | ParcelFileDescriptor.MODE_APPEND;
     else if ("rw".equals(mode)) 
        modeBits = ParcelFileDescriptor.MODE_READ_WRITE
                | ParcelFileDescriptor.MODE_CREATE;
     else if ("rwt".equals(mode)) 
        modeBits = ParcelFileDescriptor.MODE_READ_WRITE
                | ParcelFileDescriptor.MODE_CREATE
                | ParcelFileDescriptor.MODE_TRUNCATE;
     else 
        throw new IllegalArgumentException("Bad mode '" + mode + "'");
    
    return modeBits;


@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static ParcelFileDescriptor pipeFrom(InputStream inputStream, IThreadListener listener)
        throws IOException 
    ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
    ParcelFileDescriptor readSide = pipe[0];
    ParcelFileDescriptor writeSide = pipe[1];

    // start the transfer thread
    new TransferThread2(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide),
            listener)
            .start();

    return readSide;


public static ParcelFileDescriptor pipeTo(OutputStream outputStream, IThreadListener listener)
        throws IOException 
    ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
    ParcelFileDescriptor readSide = pipe[0];
    ParcelFileDescriptor writeSide = pipe[1];

    // start the transfer thread
    new TransferThread2(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream,
            listener)
            .start();

    return writeSide;


static class TransferThread2 extends Thread 
    final InputStream mIn;
    final OutputStream mOut;
    final IThreadListener mListener;

    TransferThread2(InputStream in, OutputStream out, IThreadListener listener) 
        super("ParcelFileDescriptor Transfer Thread");
        mIn = in;
        mOut = out;
        mListener = listener;
        setDaemon(true);
    

    @Override
    public void run() 
        byte[] buf = new byte[1024*8];
        int len;

        try 
            while ((len = mIn.read(buf)) > 0) 
                mOut.write(buf, 0, len);
            
            mOut.flush(); // just to be safe
         catch (IOException e) 
            Log.e("TransferThread", "writing failed");
            e.printStackTrace();
         finally 
            try 
                mIn.close();
             catch (IOException e) 
                e.printStackTrace();
            
            try 
                mOut.close();
             catch (IOException e) 
                e.printStackTrace();
            
        
        if (mListener != null)
            mListener.onThreadFinished(this);
    

奇怪的是,当您没有 root 权限时,通常会发生这种情况。但是,对于显示 pdf,您当然不需要它。

顺便说一句,这里是错误:

12-22 17:37:48.516 9112-9691/com.tproductions.Openit.pro W/System.err: java.io.IOException: write failed: EPIPE (Broken pipe)
12-22 17:37:48.516 9112-9691/com.tproductions.Openit.pro W/System.err:     at libcore.io.IoBridge.write(IoBridge.java:558)
12-22 17:37:48.516 9112-9691/com.tproductions.Openit.pro W/System.err:     at java.io.FileOutputStream.write(FileOutputStream.java:326)
12-22 17:37:48.516 9112-9691/com.tproductions.Openit.pro W/System.err:     at com.tproductions.Openit.libcore.io.IoUtils.copyLarge(IoUtils.java:310)
12-22 17:37:48.516 9112-9691/com.tproductions.Openit.pro W/System.err:     at com.tproductions.Openit.libcore.io.IoUtils.copy(IoUtils.java:283)
12-22 17:37:48.517 9112-9691/com.tproductions.Openit.pro W/System.err:     at com.tproductions.Openit.misc.ParcelFileDescriptorUtil$TransferThread.run(ParcelFileDescriptorUtil.java:70)
12-22 17:37:48.517 9112-9691/com.tproductions.Openit.pro W/System.err: Caused by: android.system.ErrnoException: write failed: EPIPE (Broken pipe)
12-22 17:37:48.517 9112-9691/com.tproductions.Openit.pro W/System.err:     at libcore.io.Linux.writeBytes(Native Method)
12-22 17:37:48.517 9112-9691/com.tproductions.Openit.pro W/System.err:     at libcore.io.Linux.write(Linux.java:286)
12-22 17:37:48.517 9112-9691/com.tproductions.Openit.pro W/System.err:     at libcore.io.BlockGuardOs.write(BlockGuardOs.java:345)
12-22 17:37:48.517 9112-9691/com.tproductions.Openit.pro W/System.err:     at libcore.io.IoBridge.write(IoBridge.java:553)
12-22 17:37:48.517 9112-9691/com.tproductions.Openit.pro W/System.err:  ... 4 more

【问题讨论】:

非常感谢,但我使用的是输入流而不是 Uri 和 mime 类型。有什么我想念的吗? 不,它只是一个内部类,只是简单的 while 循环,您可以在我的 Util 类的 thread2 中看到它 【参考方案1】:

现在问题解决了。奇怪的是,导致它的原因是我在应用程序中打开 PDF 文件的方式。我正在传递 new InputStreamDataSource() 但是,然后我关闭了输入流,这导致了异常。奇怪的是,它现在可以工作了

【讨论】:

以上是关于传递pdf文件的输入流时管道损坏的主要内容,如果未能解决你的问题,请参考以下文章

node.js html-pdf 转换问题,文件回来损坏

怎么检测PDF文件是不是损坏

如何检测PDF文档是不是损坏了?

如何检测pdf文档是不是有损坏

java 下载pdf文件损坏

为 pdf 文件创建读取流以上传到 s3 存储桶