tags:

views:

186

answers:

4

I'm trying to upload a file via URLConnection, but I need to read/write it as a binary file without any encoding changes.

So i've tried to read byte[] array from a FileInputStream, but now i have an issue. The PrintWriter object I use for outputing to the server does not allow me to do writer.write(content) (where content is of type byte[]). How can i fix this? Or is there another way to quickly copy binary data from a FileInputStream to a PrintWriter?

Thank you

+1  A: 

You should not use a PrintWriter as that is designed for text representation, and you want binary. A plain OutputStream should do, as Writers all operate on chars, in essence, text.

What do you want to achieve?

extraneon
All Writers are for textual (character) output.
R. Bemrose
I want to have a stream i could write character and binary data into. Basically i'm constructing a HTTP request that has textual parameters (form data) and binary data (files)
Marius
@Marius You actually want a stream which has both encoded binary data (the text) and unencoded binary data. The receiver has to find out which is what. In my view you should change your model and use messages, for instance implemented with XML with a structured text section and a cdata section for binary stuff.
extraneon
@Marius or of course use HTTP and send some text in headers, and binary data as content, if that model is more appropriate.
extraneon
@extraneon: The receiver is not a problem. XML is not an option, because i need a HTTP protocol compliant POST request that any web server can process. The problem is writing the data to it. Isn't there any way to output character and binary data to the same output stream?
Marius
@extraneon maybe i could use some sort of a binary stream and write character data to it with some sort of a method that takes a string and converts it to byte[]? That way i could write both textual and binary data to the same stream easily.
Marius
@Marius as long as the receiver understands the format and knows what to decode, and how, sure.
extraneon
+2  A: 

Writer objects (including PrintWriter) are intended specifically for output of character data. It sounds like you want an OutputStream instead of a Writer here.

Where did your PrintWriter come from? If it was created by wrapping some kind of OutputStream with an OutputStreamWriter and then wrapping that with a PrintWriter, then you should just use the original write(byte[] b) method from the original OutputStream, rather than trying to use a Writer.

If you want to mix character output and byte output, you may need to use String.getBytes(). Check out this example:

OutputStream o = this.conn.getOutputStream(); // Based on your comment
String s = "Hello, world!";
byte[] b = ...;      // These are the raw bytes that you want to write
o.write(s.getBytes("UTF-8"));
o.write(b);

(Of course, this will only work if the system that is reading your output understands that you are writing a mixture of characters and raw bytes and knows how to handle the mixed data that you are sending it.)

Joe Carnahan
But how would i then output character data to OutputStream? Now it's very convenient to use println().
Marius
PrintWriter is comming from:writer = new PrintWriter(new OutputStreamWriter(this.conn.getOutputStream(), "UTF-8"));
Marius
`println()` is definitely convenient, but unfortunately, it only works with encoded character data. If you want to push unencoded bytes, then you need to use `write()` instead of `println()`.
Joe Carnahan
Is there any way to pass a UTF-8 encoded string to write(), eg. converting it to byte[] or something like that? That would enable me to write textual data with write() and solve the problem with byte[] array.
Marius
See my edited answer - I was already working on a solution to that. :-)
Joe Carnahan
Thanks, now i can wrap this into a simple method and replace my println()s with it. One more question though. At the end of connection i'm doing writer.close(). If i do o.close() on the output stream, is it the same? As for the reading system, it's a standard HTTP protocol, any HTTP server will understand this mixture ;]
Marius
Yes, it should be the same. Most (Maybe all? I would have to check) stream and writer `close()` methods just delegate to the `close()` method for the stream or writer on which they were built. So, calling `writer.close()` was probably just calling `close()` on the underlying `OutputStream` already. That being said, if you're feeling really paranoid, you could insert a `o.flush()` before your `o.close()`, just to make sure all of your data gets sent before the connection is closed.
Joe Carnahan
Thank you very much, Joe. It all works nicely now.
Marius
+2  A: 

You could use 'getOutputStream()' on your URLConnection. Where is the PrintWriter coming from?

Peter Hull
PrintWriter is comming from:writer = new PrintWriter(new OutputStreamWriter(this.conn.getOutputStream(), "UTF-8"));
Marius
@Marius: Writer are used for text data, for binary data use *only* OuputStream (the same is true for Reader/InputStream). Didn't you wonder why you had to provide an encoding (`"UTF-8"`) when you only wanted to transfer binary data?
Joachim Sauer
@Joachim Sauer: Yes i did, that's why i came to this forum to find a solution for this, since sun's documentation stated that witers were not what i needed :) Anyway, the question is now solved, thank you all for your help.
Marius
+3  A: 

I bet that this is an follow-up on this question: http://stackoverflow.com/questions/2469451/upload-files-with-java

If you want to upload binary files as well using multipart/form-data, then you need to write them to the OutputStream instead. Here's the changed example of the code as I posted in your previous example, only the try block has been changed to keep a separate handle to the binary output stream, so that you can write any InputStreams to it without any encoding pains:

OutputStream output = null;
PrintWriter writer = null;
try {
    output = connection.getOutputStream();
    writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true); // true = Autoflush, important!

    writer.println("--" + boundary);
    writer.println("Content-Disposition: form-data; name=\"paramToSend\"");
    writer.println("Content-Type: text/plain; charset=UTF-8");
    writer.println();
    writer.println(paramToSend);

    writer.println("--" + boundary);
    writer.println("Content-Disposition: form-data; name=\"fileToUpload\"; filename=\"" + fileToUpload.getName() + "\"");
    writer.println("Content-Type: " + URLConnection.guessContentTypeFromName(fileToUpload.getName());
    writer.println("Content-Transfer-Encoding: binary");
    writer.println();
    InputStream input = null;
    try {
        input = new FileInputStream(fileToUpload);
        byte[] buffer = new byte[1024];
        for (int length = 0; (length = input.read(buffer)) > 0;) {
            output.write(buffer, 0, length);
        }
        output.flush();
    } finally {
        if (input != null) try { input.close(); } catch (IOException logOrIgnore) {}
    }
    writer.println();

    writer.println("--" + boundary + "--");
} finally {
    if (writer != null) writer.close();
}
BalusC
@BalusC: you're right :) I've managed to modify the code myself, although it looks different from yours now, since it's in a separate http connection class, allowing more control over what i need. By the way, isn't println os dependent (different endlines for different os'es)? And doesn't HTTP need \r\n line endings? As far as i've tested, it doesn't really care much, but still ... would be nice to do it correctly. I'll have a look through the RFC link you gave me in another post when i have enough time just in case.
Marius
It's *just* a kickoff example :) I am (we are) not going to program the whole thing in a decent OO way in 100 classes/methods complete with documentation and so on, or so ;) That's your job. And indeed, `\r\n` is specified as line separator. You can either take it in own hands or configure the system default line separator by `-Dline.separator=0xA0xD` or so.
BalusC
Yes yes i know, and it's clear enough to make a decent OO thing out of it ;] Thanks for your help ;]
Marius