0

I am trying to implement a server using Java. When I try to send a file, such as video or pdf, the connection is always reset. I am currently using OpenJDK 11 and Ubuntu.

void binaryResponse(OutputStream clientOutput, File file) throws IOException {

    int size= (int) file.length();
    String responseStr="HTTP/1.1 200 OK\r\n";
    responseStr+="Content-Type: application/force-download\r\n";
    responseStr+="Content-Length: " + size + "\r\n\r\n";

    clientOutput.write(responseStr.getBytes());

    FileInputStream fis = new FileInputStream(file);
    int bytes;
    byte[] buffer = new byte[4*1024];
    while (size > 0 && (bytes = fis.read(buffer, 0, Math.min(buffer.length, size))) != -1){
        clientOutput.write(buffer, 0, bytes);
        size -= bytes;
    }

    clientOutput.flush();
    fis.close();
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Have you debugged this? Does the file exist? "Connection reset" sounds like an `Exception` is being thrown. I recommend not to include the `size` in the loop, but just write the data to the file until `fis.read` returns `-1`. Or even simpler: use [`transferTo`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream.html#transferTo(java.io.OutputStream)). – f1sh Jan 04 '23 at 15:48
  • 1
    Also `application/force-download` might not reliably cause the client to download the data: https://stackoverflow.com/questions/10615797/utility-of-http-header-content-type-application-force-download-for-mobile – f1sh Jan 04 '23 at 15:51
  • How are you running the server and what url do you use to connect to it? – g00se Jan 04 '23 at 16:07

1 Answers1

-2

application/force-download is not a valid media type (see Utility of HTTP header "Content-Type: application/force-download" for mobile?).

You could use application/octet-stream instead, but a better option is to send the correct media type for the file type, ie application/pdf for a PDF file, etc (see How to get a file's Media Type (MIME type)?), and then send a separate Content-Disposition: attachment header to trigger a download.

Also, when creating the Content-Length header, it is more efficient to use String.valueOf() or String.format() to convert the size integer to a String, rather than letting the operator+ do the conversion implicitly (see Concatenate integers to a String in Java for why).

Try this:

void binaryResponse(OutputStream clientOutput, File file) throws IOException {
    int size = (int) file.length();
    String responseStr = "HTTP/1.1 200 OK\r\n";
    responseStr += "Content-Type: application/octet-stream\r\n";
    responseStr += "Content-Length: " + String.valueOf(size) + "\r\n";
    responseStr += "Content-Disposition: attachment; filename=\"" + file.getName() + "\"\r\n\r\n";

    clientOutput.write(responseStr.getBytes());

    FileInputStream fis = new FileInputStream(file);
    int bytes;
    byte[] buffer = new byte[4*1024];
    while (size > 0 && (bytes = fis.read(buffer, 0, Math.min(buffer.length, size))) != -1){
        clientOutput.write(buffer, 0, bytes);
        size -= bytes;
    }

    clientOutput.flush();
    fis.close();
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • @f1sh Regarding concatenation, if you read the link I posted, you'd see that using `operator+` to concat an integer creates a temporary `StringBuilder` to convert the integer to a `String`. If you had a bunch of values to concat, that would be inefficient. Better to use `String.valueOf()` for individual values, or use a single explicit `StringBuilder` to build up the final `String` entirely – Remy Lebeau Jan 05 '23 at 18:15