简体   繁体   中英

Read an InputStream from a URL and Write to HttpServletResponse without creating a temp file

I tried to read an InputStream from a URL and write to HttpServletResponse directly without creating a temporary file in an HttpServlet, but failed to get real Content-Length of the file. I've been told that I can't determine the amount of data in a stream without reading it. Can someone help me? I'd really appreciate it.

Since you are reading from a URL, you should have the length available from the Content-length header of that URL.

HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect();
if ( connection.getResponseCode() == 200 ) {
    int contentLength = connection.getContentLength();
    response.setContentLength( contentLength );

Note that you should not use inputStream.available() . The available() method merely tells you how many bytes can be read without blocking (that is, waiting for the next chunk to arrive from the network). It does not tell you the size of the stream! But in an HttpURLConnection , you can get the value of the content-length header using the getContentLength() method.

Once you do that, you can start a read-write loop to copy all the data, eg

InputStream source = connection.getInputStream();
OutputStream target = response.getOutputStream();

int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE]; 
int n =0;
while ( (n = source.read(chunk)) != -1 ) {
    target.write(chunk, 0, n);
}

Remember to use try-catch around all this, as there can be many kinds of exceptions thrown in such a transaction. And when you catch the exception, remember to set a response code in your response, which is not 200.

There's no reason you can't read the input stream from the request object until it runs out of data--buffering it into memory. In fact, in my experience, that is always how it's done; I can count on the fingers of one hand the number of times I've seen the input stream written to a file instead of being buffered into memory. If you get a content-length header, you can use that as a starting point, but I wouldn't rely on it. You'll need to protect against too much input, because you don't know what's at the far end of the input stream, and so they server you're contacting you could send so much material that you run out of RAM to process it.

Just keep calling read() until you get a -1. The other read methods can be used for this as well, but you'll add more logic to detect end-of-file.

I use this piece of code, no content-length management is done (IOUtils is in commons-io and doesn't create a temporary file) but works with small and large files:

public static void downloadFile(HttpServletResponse response, InputStream inputStream, String fileName,
        String contentType) throws IOException {

    response.reset();
    response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"",
            fileName));

    if (contentType != null) {
        response.setHeader("Content-Type", contentType);
    }

    OutputStream output = response.getOutputStream();

    IOUtils.copy(inputStream, output);

    output.flush();
    output.close();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM