How to pipe InputStream to ProcessBuilder

假装没事ソ 提交于 2019-12-30 04:37:25

问题


Please move down to the 2nd update. I didn't want to change the previous context of this question.

I'm using wkhtmltoimage from a Java app.

The standard way of using it is - path-to-exe http://url.com/ image.png.

According to their docs, if we write a - instead of an input URL, the input shifts to STDIN.

I'm starting the process using ProcessBuilder -

ProcessBuilder pb = new ProcessBuilder(exe_path, " - ", image_save_path);

Process process = pb.start();

Now I'm unable to figure out how to pipe an input stream to this process.

I have a template file read into a DataInputStream, and I'm appending a string at the end:

DataInputStream dis = new DataInputStream (new FileInputStream (currentDirectory+"\\bin\\template.txt"));
byte[] datainBytes = new byte[dis.available()];
 dis.readFully(datainBytes);
 dis.close();

 String content = new String(datainBytes, 0, datainBytes.length);

 content+=" <body><div id='chartContainer'><small>Loading chart...</small></div></body></html>";

How do I pipe content to the STDIN of the process?

UPDATE---

Following the answer by Andrzej Doyle:

I've used the getOutputStream() of the process:

ProcessBuilder pb = new ProcessBuilder(full_path, " - ", image_save_path);

    pb.redirectErrorStream(true); 

    Process process = pb.start();         

    System.out.println("reading");

    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));

    bw.write(content);

Doing so gives an error saying:

Exception in thread "main" java.io.IOException: The pipe has been ended

2nd UPDATE--------

The current code block is as such:

    try {
        ProcessBuilder pb = new ProcessBuilder(full_path, "--crop-w", width, "--crop-h", height, " - ", image_save_path);
        System.out.print(full_path+ "--crop-w"+ width+ "--crop-h"+ height+" "+ currentDirectory+"temp.html "+ image_save_path + " ");
        pb.redirectErrorStream(true); 

        Process process = pb.start(); 
        process.waitFor();
        OutputStream stdin = process.getOutputStream();

        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
// content is the string that I want to write to the process.

        writer.write(content);
        writer.newLine();  
        writer.flush();
        writer.close();


    } catch (Exception e) {
        System.out.println("Exception: " + e);
        e.printStackTrace();
    }

Running the above code gives me an IOException: The pipe is being closed.

What else do I need to do to keep the pipe open?


回答1:


Remove the whitespace from " - " -- the normal whitespaces are removed by the shell parser, but here in the ProcessBuilder, it's interpreted as the (literal) filename beginning and ending with a whitespace.

(Actually looking at the process's output as Peter suggested would probably have told you so...)




回答2:


Exception in thread "main" java.io.IOException: The pipe has been ended

This means the process you have started has died. I suggest you read the output to see why. e.g. did it give you an error.




回答3:


Is there a reason you are using DataInputStream to read a simple text file? From the Java documentation

A data input stream lets an application read primitive Java data types from an underlying input stream in a machine-independent way

It's possible that the way you are reading the file causes an EOF to be sent to the outputstream causing the pipe to end before it gets to your string.

You requirements seems to be to read the file simply to append to it before passing it on to the wkhtmltoimage process.

You're also missing a statement to close the outputstream to the process. This will cause the process to wait (hang) until it gets an EOF from the input stream, which would be never.

I'd recommend using a BufferedReader instead, and writing it directly to the outputstream before appending your additional string. Then call close() to close the stream.

ProcessBuilder pb = new ProcessBuilder(full_path, " - ", image_save_path);
pb.redirectErrorStream(true);

Process process = null;
try {
    process = pb.start();
} catch (IOException e) {
    System.out.println("Couldn't start the process.");
    e.printStackTrace();
}

System.out.println("reading");

try {
    if (process != null) {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));

        BufferedReader inputFile = new BufferedReader(new InputStreamReader(new FileInputStream(currentDirectory+"\\bin\\template.txt")));

        String currInputLine = null;
        while((currInputLine = inputFile.readLine()) != null) {
            bw.write(currInputLine);
            bw.newLine();
        }
        bw.write("<body><div id='chartContainer'><small>Loading chart...</small></div></body></html>");
        bw.newLine();
        bw.close();
    }
} catch (IOException e) {
    System.out.println("Either couldn't read from the template file or couldn't write to the OutputStream.");
    e.printStackTrace();
}

BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));

String currLine = null;
try {
    while((currLine = br.readLine()) != null) {
        System.out.println(currLine);
    }
} catch (IOException e) {
    System.out.println("Couldn't read the output.");
    e.printStackTrace();
}



回答4:


After you create the Process object, you can call getOutputStream() in order to get hold of a stream that sends its contents to the process' standard input.

Once you have hold of this you can use Java's standard IO to write whatever bytes to the this stream you want (including wrapping it in a Writer, adding buffering, etc.) - and as you write them, they'll be read by the process as soon as they're flushed.




回答5:


The following code works as well:

import java.io.*;
import java.util.*;

public class ProcessTest {

    public static void main(String[] args) throws Exception {
        ProcessBuilder pb = new ProcessBuilder("/home/me/stdinecho");
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        Process proc = pb.start();

        // Input file
        DataInputStream din = new DataInputStream((new FileInputStream("/home/me/stdinecho.cp")));
        byte[] dinBytes = new byte[din.available()];
        din.readFully(dinBytes);
        din.close();
        String content = new String(dinBytes, 0, dinBytes.length);
        content = "header\n" + content + "\nfooter";

        BufferedInputStream procStdout = new BufferedInputStream(proc.getInputStream());
        OutputStream stdin = proc.getOutputStream();

        stdin.write(content.getBytes());
        stdin.flush();
    }

}

Here stdinecho.cpp is the program that outputs the line entered on its prompt:

#include <iostream>
#include <fstream>
#include <cstdio>

using namespace std;

int main()
{
    string strOutput;
    string str;
    while(getline(cin, str)) {
        cout << str << endl;
    }
}


来源:https://stackoverflow.com/questions/11241800/how-to-pipe-inputstream-to-processbuilder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!