从进程打印 Java InputStream

Posted

技术标签:

【中文标题】从进程打印 Java InputStream【英文标题】:Printing a Java InputStream from a Process 【发布时间】:2013-03-25 22:17:40 【问题描述】:

UPDATE: I found a crucial part to why this probably isn't working! I used System.setOut(out); where out is a special PrintStream to a JTextArea

这是代码,但我遇到的问题是信息只有在我结束该过程后才会打印出来。

public Constructor() 
    main();


private void main() 
    btnStart.addActionListener(new ActionListener() 
        public void actionPerformed(ActionEvent e) 
            try 
                ProcessBuilder builder = new ProcessBuilder("java", textFieldMemory.getText(), "-jar", myJar);
                Process process = builder.start();
                InputStream inputStream = process.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream), 1);
                String line;
                while ((line = bufferedReader.readLine()) != null) 
                    System.out.println(line);
                
                inputStream.close();
                bufferedReader.close();
             catch (IOException ioe) 
                ioe.printStackTrace();
            
        
    );
 

当前输出:

Line 1
Line 2
Line 3
Line 4
Line 5

这是正确的输出,但是当我结束进程时它只被打印为一个大块。

有人知道问题出在哪里吗?如果是这样,你能帮我解释一下为什么会这样,提前谢谢你。

【问题讨论】:

输出被缓存在BufferedReader,试试直接从InputStream读取内容看看有没有区别 我同意@MadProgrammer 的观点,即问题可能出在缓冲区上。但是,我建议使用its two parameter constructor 将BufferedReader 缓冲区大小设置为1:new BufferedReader(new InputStreamReader(inputStream), 1) 使用 ByteArrayOutputStream 会更好吗?我对流完全陌生,这让我很烦恼 XD 在这种情况下,我只使用一个小的byte 数组并直接从InputStream 中读取。我这样做的主要原因是因为并非所有进程都将换行符作为输出的一部分发送,这意味着有时,您没有得到任何输出,并且进程似乎永远不会死 我怀疑你的缓冲阅读器是问题所在。两个简单的问题:你在使用process.waitFor吗?您是否在单独的线程上读取流输出? 【参考方案1】:

在单独的线程中处理进程的输出流可能会有所帮助。您还想明确等待流程结束,然后再继续执行您的逻辑:

ProcessBuilder builder = new ProcessBuilder("java",
        textFieldMemory.getText(), "-jar", myJar);
final Process process = builder.start();
final Thread ioThread = new Thread() 
    @Override
    public void run() 
        try 
            final BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) 
                System.out.println(line);
            
            reader.close();
         catch (final Exception e) 
            e.printStackTrace();
        
    
;
ioThread.start();

process.waitFor();

【讨论】:

对我不起作用,就像我说的那样,我认为这与我将控制台输出重定向到 JTextArea 有关。 是的,这是一个非常重要的细节。您很可能必须在每次写入时显式刷新流,然后触发 JTextArea 的重绘,以便实时显示输出。 如果您查看问题中的原始代码并将inputStream.close(); 移动到while 循环中,我发现了一些有趣的东西,我成功地在JTextArea 中打印了第一行,然后流显然是关闭。 好吧,close 做了一个flush,所以尝试这样做,看看它对程序有何影响。 不幸的是 InputStream 没有flush 方法:/【参考方案2】:

基本上,从仅有的少量信息来看,这听起来像是您正在执行进程并从事件调度线程中读取InputStream

任何阻止 EDT 的东西都会阻止它处理重绘请求,相反,您应该使用 SwingWorker 之类的东西,它具有允许您使用 EDT 更新 UI 的功能。

查看Concurrency in Swing了解更多详情

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class PBDemo 

    public static void main(String[] args) throws Exception 
        new PBDemo();
    

    public PBDemo() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) 
                

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            
        );
    

    public class TestPane extends JPanel 

        public TestPane() 
            setLayout(new BorderLayout());
            JTextArea ta = new JTextArea();
            add(new JScrollPane(ta));

            new ProcessWorker(ta).execute();
        

        @Override
        public Dimension getPreferredSize() 
            return new Dimension(200, 200);
        
    

    public interface Consumer 
        public void consume(String value);            
    

    public class ProcessWorker extends SwingWorker<Integer, String> implements Consumer 

        private JTextArea textArea;

        public ProcessWorker(JTextArea textArea) 
            this.textArea = textArea;
        

        @Override
        protected void process(List<String> chunks) 
            for (String value : chunks) 
                textArea.append(value);
            
        

        @Override
        protected Integer doInBackground() throws Exception 
            // Forced delay to allow the screen to update
            Thread.sleep(5000);
            publish("Starting...\n");
            int exitCode = 0;
            ProcessBuilder pb = new ProcessBuilder("java.exe", "-jar", "HelloWorld.jar");
            pb.directory(new File("C:\\DevWork\\personal\\java\\projects\\wip\\***\\HelloWorld\\dist"));
            pb.redirectError();
            try 
                Process pro = pb.start();
                InputConsumer ic = new InputConsumer(pro.getInputStream(), this);
                System.out.println("...Waiting");
                exitCode = pro.waitFor();

                ic.join();

                System.out.println("Process exited with " + exitCode + "\n");

             catch (Exception e) 
                System.out.println("sorry" + e);
            
            publish("Process exited with " + exitCode);
            return exitCode;
        

        @Override
        public void consume(String value) 
            publish(value);
        
    

    public static class InputConsumer extends Thread 

        private InputStream is;
        private Consumer consumer;

        public InputConsumer(InputStream is, Consumer consumer) 
            this.is = is;
            this.consumer = consumer;
            start();
        

        @Override
        public void run() 
            try 
                int in = -1;
                while ((in = is.read()) != -1) 
//                    System.out.print((char) in);
                    consumer.consume(Character.toString((char)in));
                
             catch (IOException exp) 
                exp.printStackTrace();
            
        
    

【讨论】:

对我不起作用,就像我说的那样,我认为这与我将控制台输出重定向到 JTextArea 有关。 没有提到JTextArea?您是否在 EDT 中执行该流程? 对不起,是的,我意识到这是导致问题的关键部分,正如我之前评论的那样。而且我们还没有涵盖我课程中的主题,抱歉> 为this answer 借用的代码用于类似问题。非常感谢! @HovercraftFullOfEels 我什么都不知道,我“借”了一切;)

以上是关于从进程打印 Java InputStream的主要内容,如果未能解决你的问题,请参考以下文章

java多线程死锁

Python - 如何从打印信息的单独进程中实时读取类实例

在java WorkbookFactory中编辑excel

java poi 读取有密码加密的Excel文件

java杂项

在linux环境下,java怎么实现从word格式转换为pdf格式