Sending Binary and Textdata via HTTP POST - java

I try to use HTTP POST to send some Data to a Server.
The Server expects the binary Data in $_POST['file'].
URL url = new URL("http://example.com");
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
OutputStream outputStream = connection.getOutputStream();
outputStream.write("file=".getBytes());
//byte[] buffer contains the data
outputStream.write(buffer);
outputStream.close();
Is OutputStream.write the right method to write into the stream? Do I have to handle the string ("file=") other then the buffer?

I recommend converting your data to Base64 String (Compatibility with all systems).
string result = Convert.ToBase64String(Encoding.UTF8.GetBytes(utf8Text));

Yes, to write text with POST, you will need to write to `OutputStream.
For parameters, you will need to create a String of key-value pair (separated with &) and write the byte array of that data in OutputStream as follows:
String parameterString = "file=" + parameters.get("file") + "&" + "other=" + parameter.get("other");
outputStream.write(parameterString.getBytes("UTF-8"); //Don't forget, HTTP protocol supports UTF-8 encoding.
outputStream.flush();
To do file upload with URLConnection, see BalusC's article How to use java.net.URLConnection to fire and handle HTTP requests?

Related

Java program is not able to write complete web content, however in SOAPUI, I can see complete content

I wrote a Java program to make a simple HTTP get call and read content from a REST webservice. I include 2 custom HTTP headers and provide values to them.
In SOAPUI, when I make a REST call with updating those 2 headers, I get proper response, however when I make the same call in my Java program, I get truncated output. Below is my code:
try {
URL url = new URL(lnk);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("HeaderParameter1", param1);
connection.setRequestProperty("HeaderParamter2", param2);
connection.setConnectTimeout(60000);
InputStream is = connection.getInputStream();
byte [] b = new byte [is.available()];
is.read(b);
String input = new String(b);
System.out.println("The payload is \n"+input);
connection.disconnect();} catch(Exception e){}
Calling is.available() returns the number of bytes that are available to be read without blocking, but that may be less than the full content of the response. In the documentation for this method, it says: "It is never correct to use the return value of this method to allocate a buffer intended to hold all data in this stream."
A simple way to read the entire response into a String could be:
InputStream is = connection.getInputStream();
byte [] b = is.readAllBytes();
String input = new String(b);
There are two caveats with this approach:
The new String(byte[]) constructor uses the platform's default charset to convert the bytes to characters. This is generally not a good idea for network protocols, since the client and server need to agree on the character encoding. You can hardcode the charset encoding if it will always be the same, for example: new String(b, "UTF-8").
If the response is very large, this will allocate a large byte array. If your handling of the response can be written in a streaming manner, it would be better to read iteratively with a fixed buffer size, as Anders mentioned in his comment.

HttpURLConnection : How does client and server keep in sync

I am using HttpURLConnection and I have a problem understanding on how client and server sync. Assume simple download file example. This example is copied somewhere from web. I am only using the code to state the standard process.
Servlet code is like:
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.length());
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", downloadFile.getName());
response.setHeader(headerKey, headerValue);
// obtains response's output stream
OutputStream outStream = response.getOutputStream();
//write to stream
//close the stream
And Client code is like :
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
int responseCode = httpConn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
String disposition = httpConn.getHeaderField("Content-Disposition");
String contentType = httpConn.getContentType();
int contentLength = httpConn.getContentLength();
//parse content-disposition
....
InputStream inputStream = httpConn.getInputStream();
String saveFilePath = saveDir + File.separator + fileName;
// opens an output stream to save into file
FileOutputStream outputStream = new FileOutputStream(saveFilePath);
//write to stream
//close stream
} else {
//if non Ok status
}
My first question is : Is httConn.getResponseCode() a blocking call that waits for the servlet to finish processing? Otherwise, if error comes or servlet call response.sendError(), when you are inside if (responseCode == HttpURLConnection.HTTP_OK) {, what will happen.
Second Question : is an extension of first question. If responseCode is not blocking, then when i am accessing disposition, contentType, cotentLength, how am i sure that they are already set.
Third Question. If httConn.getResponseCode() is blocking. So if I want to send send some message to the client, how correct is to send it to the client in response headers like : resposnse.setHeader("my-message", "some message I want to send"); rather than using response.getWriter() to write to the stream. So that I am sure client will definitely read that.
Fourth Question : If I am writing two objects to streams on servlet, how will client distinguish or can it distinguish? Suppose I am writing a class object using response.getObjectOutputStream() and then I may be writing some string using writer or may be write a file after that. Can client distinguish these different items coming in stream or do I have to use multiple request. One request per object or file or String to be read from stream.
Yes, as the javadoc indicates
Gets the status code from an HTTP response message. For example, in the case of the following status lines:
HTTP/1.0 200 OK
HTTP/1.0 401 Unauthorized
It will return 200 and 401 respectively. Returns -1 if no code can be discerned from the response (i.e., the response is not valid HTTP).
NA
You can use headers if you want, but headers are limited to text only and limited in length (AFAIR). The response body is usually used to contain... the response body. Whereas headers are typically used for metadata.
server and client have to agree on a protocol. If the protocol is that a response contains two objects, then the client should read two objects. I would not do that though. You'd better send a unique container object rather than 2 in sequence. HTTP can be used to transport anything, but a JSON or XML document is usually used to transport structured data.

HTTPUrl connection closed automatically

i am reading a xml response using httpurlconnection. i parsed the response using JAXB. I didnt close the connection. When i again try to read from the URL , I am getting the error as Input stream. Do i have to open connection twice or is there any way to open connection once and read the response twice and then close the connection?
JAXB likely consumes the InputStream and then closes it. You would need to use some kind of FilterInputStream so that it's buffered and reusable.
With Guava, you can do something like
HttpURLConnection con = ...; // get it
InputStream in = con.getInputStream();
String content = CharStreams.toString(new InputStreamReader(in, Charsets.UTF_8));
Then pass a new InputStream to JAXB made from the bytes of content.
InputStream inForJAXB = new ByteArrayInputStream(content.getBytes());
You can do the same thing again for any other component that needs the content of the HttpURLConnection input stream.

Java's URLConnection not receiving entire binary file

I am currently working on a school that encompasses creating a P2P client for a standard we came up with in class that uses HTTP to request chunks of a binary file from peers. We are allowed to us Java's HTTP libraries to make these requests, however I am hitting a major problem with these libraries. All chunks of a file will be served up in chunks that are <=64KB, but when I use the following code, the max amount of bytes that I receive is around 15040 even though the content-length of the response is 64KB:
String response = "";
URL url = new URL(uriPath);
URLConnection conn = url.openConnection ();
conn.setConnectTimeout(30 * 1000);
conn.setReadTimeout(30 * 1000);
InputStream stream = conn.getInputStream();
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
int c;
byte[] buffer = new byte[4096];
while ((c = stream.read(buffer)) != -1)
{
byteArrayOut.write(buffer,0,c);
}
body = byteArrayOut.toByteArray();
stream.close();
result.put(Constants.HEADER_CONTENT_LENGTH, conn.getHeaderField(Constants.HEADER_CONTENT_LENGTH));
result.put(Constants.HEADER_CONTENT_CHECKSUM, conn.getHeaderField(Constants.HEADER_CONTENT_CHECKSUM));
result.put(Constants.KEY_BODY, new String(body));
We've tested our server component, and that serves the file correctly when accessing a chunk with wget or in a browser - this java client is the only problematic client we were able to find.
Is this a problem with Java's URLConnection class, or is there something in my code that is wrong with reading a binary file that is returned in a response?
Note: I am using Java 1.6 in Eclipse and from the command line.
How do you know that the max amount of bytes is 15040? Did you byteArrayOut.toByteArray().length or did you do new String(byteArrayOut.toByteArray()).length()?
Creating a new String from a byte array that has binary content is likely to give unpredictable results. Use a FileOutputStream and open the file.

Writing post data from one java servlet to another

I am trying to write a servlet that will send a XML file (xml formatted string) to another servlet via a POST.
(Non essential xml generating code replaced with "Hello there")
StringBuilder sb= new StringBuilder();
sb.append("Hello there");
URL url = new URL("theservlet's URL");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Length", "" + sb.length());
OutputStreamWriter outputWriter = new OutputStreamWriter(connection.getOutputStream());
outputWriter.write(sb.toString());
outputWriter.flush();
outputWriter.close();
This is causing a server error, and the second servlet is never invoked.
This kind of thing is much easier using a library like HttpClient. There's even a post XML code example:
PostMethod post = new PostMethod(url);
RequestEntity entity = new FileRequestEntity(inputFile, "text/xml; charset=ISO-8859-1");
post.setRequestEntity(entity);
HttpClient httpclient = new HttpClient();
int result = httpclient.executeMethod(post);
I recommend using Apache HTTPClient instead, because it's a nicer API.
But to solve this current problem: try calling connection.setDoOutput(true); after you open the connection.
StringBuilder sb= new StringBuilder();
sb.append("Hello there");
URL url = new URL("theservlet's URL");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Length", "" + sb.length());
OutputStreamWriter outputWriter = new OutputStreamWriter(connection.getOutputStream());
outputWriter.write(sb.toString());
outputWriter.flush();
outputWriter.close();
The contents of an HTTP post upload stream and the mechanics of it don't seem to be what you are expecting them to be. You cannot just write a file as the post content, because POST has very specific RFC standards on how the data included in a POST request is supposed to be sent. It is not just the formatted of the content itself, but it is also the mechanic of how it is "written" to the outputstream. Alot of the time POST is now written in chunks. If you look at the source code of Apache's HTTPClient you will see how it writes the chunks.
There are quirks with the content length as result, because the content length is increased by a small number identifying the chunk and a random small sequence of characters that delimits each chunk as it is written over the stream. Look at some of the other methods described in newer Java versions of the HTTPURLConnection.
http://java.sun.com/javase/6/docs/api/java/net/HttpURLConnection.html#setChunkedStreamingMode(int)
If you don't know what you are doing and don't want to learn it, dealing with adding a dependency like Apache HTTPClient really does end up being much easier because it abstracts all the complexity and just works.
Don't forget to use:
connection.setDoOutput( true)
if you intend on sending output.

Categories