How to Pass a File through an HttpURLConnection - java

I'm trying to get an image hosting on our server available to be displayed on a client. As per the specs of the project:
"When a Client receives such a URL, it must download the
contents (i.e., bytes) of the file referenced by the URL.
Before the Client can display the image to the user, it must first retrieve (i.e., download) the bytes of the
image file from the Server. Similarly, if the Client receives the URL of a known data file or a field help file
from the Server, it must download the content of those files before it can use them."
I'm pretty sure we have the server side stuff down, because if I put the url into a browser it retrieves and displays just fine. So it must be something with the ClientCommunicator class; can you take a look at my code and tell me what the problem is? I've spent hours on this.
Here is the code:
Where I actually call the function to get and display the file: (This part is working properly insofar as it is passing the right information to the server)
JFrame f = new JFrame();
JButton b = (JButton)e.getSource();
ImageIcon image = new ImageIcon(ClientCommunicator.DownloadFile(HOST, PORT, b.getLabel()));
JLabel l = new JLabel(image);
f.add(l);
f.pack();
f.setVisible(true);
From the ClientCommunicator class:
public static byte[] DownloadFile(String hostname, String port, String url){
String image = HttpClientHelper.doGetRequest("http://"+hostname+":"+port+"/"+url, null);
return image.getBytes();
}
The pertinent httpHelper:
public static String doGetRequest(String urlString,Map<String,String> headers){
URL url;
HttpURLConnection connection = null;
try {
//Create connection
url = new URL(urlString);
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Language", "en-US");
connection.setUseCaches (false);
connection.setDoInput(true);
connection.setDoOutput(true);
if(connection.getResponseCode() == 500){
return "failed";
}
//Get Response
InputStream is = connection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuffer response = new StringBuffer();
while((line = rd.readLine()) != null) {
response.append(line);
}
rd.close();
return response.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if(connection != null) {
connection.disconnect();
}
}
}
After that, it jumps into the server stuff, which as I stated I believe is working correctly because clients such as Chrome can retrieve the file and display it properly. The problem has to be somewhere in here.
I believe that it has to do with the way the bytes are converted into a string and then back, but I do not know how to solve this problem. I've looked at similar problems on StackOverflow and have been unable to apply them to my situation. Any pointers in the right direction would be greatly appreciated.

If your server is sending binary data, you do not want to use an InputStreamReader, or in fact a Reader of any sort. As the Java API indicates, Readers are for reading streams of characters (not bytes) http://docs.oracle.com/javase/7/docs/api/java/io/Reader.html, which means you will run into all sorts of encoding issues.
See this other stack overflow answer for how to read bytes from a stream:
Convert InputStream to byte array in Java

Do your homework.
Isolate the issue. Modify the server side to send only 256 all possible bytes. Do a binary search and reduce it to small set of bytes.
Use http proxy tools to monitor the bytes as they are transmitted. Fiddler in windows world. Find other ones for the *nix environments.
Then see where the problem is happening and google/bing the suspicions or share the result.

Related

Json response from URL too big

Trying to consume json response from url runs out of memory, or have to declare android:largeHeap="true".
This is the url
https://www.mtgjson.com/json/AllCards.json
When I enter this url in web browser on my PC, the page is populated within a second. why does it take android such long time to finish this api request? EDIT: I realize the web browser does not load all the data immediately, i see it showing the data as it comes before scrolling down in the window.
Are there ways to make the android device respond as fast as the web browser on pc when accessing this url?
When using emulators, outOfMemoryExeption unless if I increase RAM size. on samsung S7 device, outOfMemoryException unless I have largeHeap="true" in the manifest.
Same url in web browser doesn't take as long.
#Override
protected String doInBackground(String... params) {
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
String apiJsonStr = null;
try {
String baseUrl = "https://www.mtgjson.com/json/AllCards.json";
String uri = Uri.parse(baseUrl).buildUpon().toString();
URL url = new URL(baseUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
return null;
}
apiJsonStr = buffer.toString();
} catch (IOException e) {
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
}
}
return apiJsonStr;
}
#Override
protected void onPostExecute(String stringReturn) {
super.onPostExecute(stringReturn);
mListener.allCardsResponse(stringReturn);
}
Before you venture that route of trying to fit a square peg in a round hole by using a big hammer to smash the hole. I suggest you think of a better approach.
First of all, I looked at the json document. My browser took 1 min to load it and browser these days have gotten pretty efficient so they start to stream chunks they receive immediately. Second the file in 65.5 MB uncompressed.
This is a disaster ready to happen when you publish the app. User experience is going to suck. Everyone internet speed is going to be different and not to mention their connection proximity to your server (network latency). If it's going to take forever to load this thing if it's happening synchronously which it looks like it is, user are going to abandon the app.
Here is what I propose you look into -
If you have control over URL which returns the JSON, see if you can
use pagination and only return a fraction that you are going to
display in your mobile app. I'm pretty sure you can't cram all these
information a 6' - 9' mobile screen.
Perhaps you can transform this endpoint to only return few interesting metadata and when users show interest, you pull down all the details of that specific metadata (which will be one json object in your list)? That's assuming you control the API.
If this is an API that you don't control, you can create an
intermediary service that will do #1 for you so you can better
optimize it.
Another approach if you don't control the URL and the content is
static enough you can package with your app or configure out a
background sync that chunks these into 1 MB each and works
asynchronously.

HttpURLConnection FileNotFoundException on large request properties

I'm using HttpURLConnection to send JSON data from an Android Application to my Tomcat Server.
The POST works fine with small sized JSONs. On bigger data sets it fails with a FileNotFoundException.
What can it be?
Here's the code:
try {
URL url = new URL(urlIn);
strOut = "";
huc = (HttpURLConnection) url.openConnection();
huc.setRequestProperty("Connection", "Close");
huc.setRequestMethod("POST");
huc.setRequestProperty("User", userId);
huc.setRequestProperty("Action", action);
huc.setRequestProperty("JSON", jsonData);
huc.setConnectTimeout(10000);
in = new BufferedReader(new InputStreamReader(huc.getInputStream()));
while ((inputLine = in.readLine()) != null){
if (strOut.equalsIgnoreCase("")){
strOut = inputLine;
} else {
strOut = strOut + inputLine;
}
}
} catch (Exception e) {
strOut = "";
e.printStackTrace();
}
When jsonData get to a certain size (arround 10000 chars), the POST fails with the error mentioned. The content of the JSON does not have any special character.
Thanks in advance.
Best regards, Federico.
HTTPUrlConnection throws a FileNotFoundException if the server responds with a 404 response code, so the reason why this happens seems to be located on the server side rather than the client side. Most likely the server is configured to accept request headers up to a particular length and will return an error if that size is exceeded. A short Google-search brought up a couple of results, sizes of 16 KB are mentioned but shorter values are also reasonable.
As I mentioned in my comment to your question, you should change your process to receive the JSON-data (and the other values for User and Action as well BTW) as part of the request body, e.g. as url-encoded query string or as multipart formdata. Both ways are supported by HTTP client libraries you can use or are easily built manually.
After lots of reading and trying I gave up with configuring Tomcat to accept larger headers.
So I convinced the team in charge of the Tomcat app to make a servlet that is able to receive this data in the body, just as Lothar suggested.
Thanks!

Playing radiostreams using javax.media.Player vs. javazoom.jl.player.Player

I am new to playing audio with Java. I have written code to play some radio streams.
I find that there are some streaming urls like http://fm939.wnyc.org/wnycfm. This has no port number and contains slashes. I am able to play this type of url only with javax.media.Player.
There are other streaming urls that come with a port number and no slashes. For example, I have a url for National Public Radio 140.254.23.68:8000. I can play these types of url with javazoom.jl.player.Player since this player takes a url string and a port number.
Can someone tell me a little more about the types of streams and how to use the above players correctly. For example, is it possible to play the stream http://fm939.wnyc.org/wnycfm with javazoom.jl.player.Player ? If so, how?
Any help will be much appreciated.
Addendum:
Sorry I didn't mean to say javazoom Player accepts a url. I am using the following code snippet to create a javazoom player. As you can see from the code, I am using SocketFactory createSocket method to create a connection. The createSocket method takes a url and port. I'd like to know how to play a url like http://fm939.wnyc.org/wnycfm, in other words, a url without an explicit port number.
see code snippet below-
response = null;
try {
SocketFactory sf = SocketFactory.getDefault();
connection = sf.createSocket(url,port);
request = "GET / HTTP/1.1\n\n";
outputStream = connection.getOutputStream();
if(outputStream!=null) {
outputStream.flush();
byte[] b = null;
try {b = request.getBytes(StandardCharsets.US_ASCII);}
catch(NullPointerException npe) {..}
if(b != null) {
outputStream.write(b);
outputStream.flush();
response = connection.getInputStream();
}
} catch (IOException e) {e.printStackTrace();}
javazoom.jl.player.Player zoomPlayer = null;
if(response!=null) {
try {zoomPlayer = new javazoom.jl.player.Player(response);
} catch (JavaLayerException e) {e.printStackTrace();}
}
return zoomPlayer;
"A URL can optionally specify a "port"".
so you can set the port.
I dont see where javazoom.jl.player.Player uses a url - maybe a newer version.
But in any case see the document for URL. One of the constructors says
URL(String protocol, String host, int port, String file, URLStreamHandler handler)
Creates a URL object from the specified protocol, host, port number, file, and handler.
The jmf player can be considered more stable. and documented.
javazoom.jl.player.Player is one line.
--
In fact I can play both streams with regular javax.sound procedures.
--
The way to get the url stream is this:
String u="http://140.254.23.68:8000";
URL url=new URL(u);
URLConnection uc = new URL(u).openConnection();
InputStream is=(InputStream)uc.getInputStream();

HttpsURLConnection Connection Problems

I'm a problem with a HttpsURLConnection that I can't seem to solve. Basically, I'm sending up some info to a server and if some of that data is wrong, the server sends me a 500 response code. However, it also sends a message in the response telling me which bit of data was wrong. The problem is that the message is always empty when I read it in. I think this is because a filenotfound exception always gets thrown before the stream can be read. Am I right? I tried reading the errorstream as well but this is always empty. Here's a snippet:
conn = (HttpsURLConnection) connectURL.openConnection();
conn.setDoOutput(true);
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Length",
Integer.toString(outString.getBytes().length));
DataOutputStream wr = new DataOutputStream(conn
.getOutputStream());
wr.write(outString.getBytes());
wr.flush();
wr.close();
if(conn.getResponseCode>400{
String response = getErrorResponse(conn);
public String getErrorResponse(HttpsURLConnection conn) {
Log.i(TAG, "in getResponse");
InputStream is = null;
try {
//is = conn.getInputStream();
is = conn.getErrorStream();
// scoop up the reply from the server
int ch;
StringBuffer sb = new StringBuffer();
while ((ch = is.read()) != -1) {
sb.append((char) ch);
}
//System.out.println(sb.toString());
return sb.toString();
// return conferenceId;
}
catch (Exception e){
e.printStackTrace();
}
}
So just to follow up on this, here is how I solved it:
public static String getResponse(HttpsURLConnection conn) {
Log.i(TAG, "in getResponse");
InputStream is = null;
try {
if(conn.getResponseCode()>=400){
is = conn.getErrorStream();
}
else{
is=conn.getInputStream();
}
...read stream...
}
It seems that calling them like this produced an error stream with a message. Thanks for the suggestions!
Try setting content-type request property to "application/x-www-form-urlencoded"
The same is mentioned on this link:
http://developers.sun.com/mobility/midp/ttips/HTTPPost/
The Content-Length and Content-Type headers are critical because they tell the web server how many bytes of data to expect, and what kind, identified by a MIME type.
In MIDP clients the two most popular MIME types are application/octet-stream, to send raw binary data, and application/x-www-form-urlencoded, to send name-value pairs
Are you in control of the server? In other words, did you write the process that runs on the server and listens to the port you're trying to access?
If you did, then you should also be able to debug it and see why your process returns 404.
If you didn't, then describe your architecture (HTTP server, the component it invokes to respond to your HTTP(S) request, etc) and we'll take it from there.
In the very simplest case, of an HTTP server being an Apache server yielding control to some PHP script, it means that Apache couldn't assign your request to anything. Most likely a Web server misconfiguration. Provide some more details and we'll help you out.

how to (simply) generate POST http request from java to do the file upload

I would like to upload files from java application/applet using POST http event. I would like to avoid to use any library not included in SE, unless there is no other (feasible) option.
So far I come up only with very simple solution.
- Create String (Buffer) and fill it with compatible header (http://www.ietf.org/rfc/rfc1867.txt)
- Open connection to server URL.openConnection() and write content of this file to OutputStream.
I also need to manually convert binary file into POST event.
I hope there is some better, simpler way to do this?
You need to use the java.net.URL and java.net.URLConnection classes.
There are some good examples at http://java.sun.com/docs/books/tutorial/networking/urls/readingWriting.html
Here's some quick and nasty code:
public void post(String url) throws Exception {
URL u = new URL(url);
URLConnection c = u.openConnection();
c.setDoOutput(true);
if (c instanceof HttpURLConnection) {
((HttpURLConnection)c).setRequestMethod("POST");
}
OutputStreamWriter out = new OutputStreamWriter(
c.getOutputStream());
// output your data here
out.close();
BufferedReader in = new BufferedReader(
new InputStreamReader(
c.getInputStream()));
String s = null;
while ((s = in.readLine()) != null) {
System.out.println(s);
}
in.close();
}
Note that you may still need to urlencode() your POST data before writing it to the connection.
You need to learn about the chunked encoding used in newer versions of HTTP. The Apache HttpClient library is a good reference implementation to learn from.

Categories