Receive ByteArray in Actionscript 3 from Java Servlet - java

I was typing a question but finally I solved the problem and don't wanted to toss it (and encouraged by https://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/), and decided to share my problem-solution.
The problem is that I want to retrieve some bytes from a Java application server, that means, via a Servlet to load in a flash game for a replay feature.
There are some questions trying to solve the other way problem, that means, from as3 to a server (php, java, etc): How to send binary data from AS3 through Java to a filesystem?, How can I send a ByteArray (from Flash) and some form data to php?, Uploading bytearray via URLRequest and Pushing ByteArray to POST. I didn't find something like what I'm sharing (correct me if I'm wrong).

Well, as I said in the question, I was encouraged by StackOverflow to answer and here it is:
The Servlet doGet method that gives the byte array:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
MouseInput oneInput = getMouseInput(); //abstracted (I'm using google appengine)
byte[] inputInBytes = oneInput.getInBytes();
OutputStream o = resp.getOutputStream();
o.write(inputInBytes);
o.flush();
o.close();
}
MouseInput.getInBytes method body:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(this.type);
dos.writeDouble(this.localX);
dos.writeDouble(this.localY);
dos.writeBoolean(this.buttonDown);
return baos.toByteArray();
My Actionscript code to receive the byte array data:
var url:String = "http://localhost:8888/input"; //servlet url
var request:URLRequest = new URLRequest(url);
//get rid of the cache issue:
var urlVariables:URLVariables = new URLVariables();
urlVariables.nocache = new Date().getTime();
request.data = urlVariables;
request.method = URLRequestMethod.GET;
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, function (evt:Event) {
var loader:URLLoader = URLLoader(evt.target);
var bytes:ByteArray = loader.data as ByteArray;
trace(bytes); //yeah, you'll get nothing!
//the bytes obtained from the request (see Servlet and
//MouseInput.getInBytes method body code above) were written in
//the sequence like is read here:
trace(bytes.readInt());
trace(bytes.readDouble());
trace(bytes.readDouble());
trace(bytes.readBoolean());
}
loader.addEventListener(IOErrorEvent.IO_ERROR, function (evt:Event) {
trace("error");
});
loader.load(request);
Well, it works! Obviously you can make some adjustments, like not using the anonymous function for better reading, but to illustrate it was ok! Now I can save some memory to a game replay feature (for debug purpose) with ByteArray instead of heavy XML that I was trying.
Hope it helped and any critics is appreciated!
Cheers

Related

ByteArrayOutputStream: Odd behavior

I'm writing a simple client-server application and I wanted to be able to take the attributes of a Header class, turn them into a byte[], send them to the other host, and then convert them back into an easily parsed Header. I was using a ByteArrayOutputStream to do this, but the results were not what I expected. For example, just to test it in main() I had:
Header h = Header();
h.setSource(111);
h.setDest(222);
h.setSeq(333);
h.setAck(444);
byte[] header = Header.convertHeaderToByteArray();
Header newHeader = new Header(headerArray);
Where convertHeaderToByteArray() looked like:
public byte[] convertHeaderToByteArray() {
byte[] headerArray;
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(44);
byteStream.write(srcPort);
byteStream.write(dstPort);
byteStream.write(seqNum);
byteStream.write(ackNum);
byteStream.write(controlBits);
headerArray = byteStream.toByteArray();
return headerArray;
}
And the Header(headerArray) constructor:
public Header(byte[] headerArray) {
ByteArrayInputStream header = new ByteArrayInputStream(headerArray);
srcPort = header.read();
dstPort = header.read();
seqNum = header.read();
ackNum = header.read();
}
This definitely did not behave as expected. When I looked at those values at the end, srcPort was correct (111), dstPort was correct (222), seqNum was not correct (77), and ackNum was not correct (188).
After hours of reading and tinkering I couldn't get it right, so I tried to use ByteBuffer instead. Viola, correct results.
What is going on here? I read the documentation for both and although I spotted some differences I'm not seeing what the source of my error is.
OutputStream.write(int) writes a single byte. See the Javadoc. If you want to write wider values, you will have to use the writeXXX() methods of DataOutputStream, and the corresponding readXXX() methods of DataInputStream to read them.

How to convert byte array to buffered image

I have a server-side java code that gets a byte array from the client. In order to do some image processing, I need to convert the byte array into a BufferedImage. I have a code that's supposed to do that here:
public void processImage(byte[] data) {
ByteArrayInputStream stream = new ByteArrayInputStream(data);
BufferedImage bufferedImage;
bufferedImage = ImageIO.read(stream);
// bufferedImage is null
//...
}
But this doesn't work; bufferedImage is null. According to the ImageIO documentation:
If no registered ImageReader claims to be able to read the resulting stream, null is returned.
How do I tell the ImageReader what image type it is. For instance, if I know the image to be JPEG (which it is, in my case), what am I supposed to do?
EDIT: Thanks for the suggestion that the file is most likely not in JPEG format. This is the client-side code I have that sends the data as String over to the server:
import org.json.JSONObject;
// Client-side code that sends image to server as String
public void sendImage() {
FileInputStream inputStream = new FileInputStream(new File("myImage.jpg"));
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] b = new byte[1024];
while ((bytesRead = inputStream.read(b)) != -1) {
byteStream.write(b,0,bytesRead);
}
byte[] byteArray = byteStream.toByteArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("data",new String(byteArray));
// ... more code here that sends jsonObject in HTTP post body
}
And this is the server-side code that calls the processImage() function:
// Server-side code that calls processImage() function
public void handleRequest(String jsonData) {
JSONObject jsonObject = new JSONObject(jsonData);
processImage(jsonObject.getString("data").getBytes());
}
The most likely explanation is that the byte array doesn't contain a JPEG image. (For instance, if you've just attempted to download it, you may have an HTML document giving an error diagnostic.) If that's the case, you'll need to find what is causing this and fix it.
However, if you "know" that the byte array contains an image with a given format, you could do something like this:
Use ImageIO.getImageReadersByFormatName or ImageIO.getImageReadersByMIMEType to get an Iterator<ImageReader>.
Pull the first ImageReader from the Iterator.
Create an MemoryCacheImageInputStream wrapping a ByteArrayInputStream for the types.
Use ImageReader.setInput to connect the reader to the ImageInputStream.
Use ImageReader.read to get the BufferedImage.

Raw Image Data as POST param using AsyncHttpClient

I have a simple PHP service I'm trying to hit from an Android app, and I would like to pass a raw image via a POST param. I have a PHP/curl script working which does the following:
$url = "http://myphp.php"
$imagefilepath = 'path_to_png_file.png';
$imagedata = file_get_contents($imagefilepath);
$data = array('imagedata' => $imagedata);
// a few other fields are set into $data, but not important
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 GTB5');
curl_exec($ch);
I want to mimic this very thing in Java from my Android app, using AsyncHttpClient (http://loopj.com/android-async-http/).
Sounds simple enough, and I can get the call working in Java, but the issue is that the data I send is not recognized on the other end as an image. With the above PHP/Curl script, however, it works fine on all fronts.
Here is my Java code, with a few commented-out things I've tried:
String photoFilePath = "path_to_my_photo_on_disk.jpg";
Bitmap bm = BitmapFactory.decodeFile(photoFilePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 50, baos);
byte[] byteArrayPhoto = baos.toByteArray();
AsyncHttpClient client = new AsyncHttpClient(context);
RequestParams params = new RequestParams();
try {
// THINGS I HAVE TRIED (AND FAILED):
//params.put("imagedata", new File(photoFilePath));
//params.put("imagedata", new ByteArrayInputStream(byteArrayPhoto), "photo.jpg");
//params.put("imagedata", new String(byteArrayPhoto));
//params.put("imagedata", new String(byteArrayPhoto, "UTF-8"));
//params.put("imagedata", fileToString(photoFilePath));
//params.put("imagedata", new FileInputStream(new File(photoFilePath)), "photo.jpg", "image/jpeg");
} catch (Exception e) {
e.printStackTrace();
}
client.post(context, myURL, params, new AsyncHttpResponseHandler() {
...override methods, onSuccess() is called...
}
// for reference, for the above-called method:
private String fileToString(String filename) throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader(filename));
StringBuilder builder = new StringBuilder();
String line;
// For every line in the file, append it to the string builder
while((line = reader.readLine()) != null)
{
builder.append(line);
}
return builder.toString();
}
Note that on a few of the commented-out lines/attempts above, the POST parameter doesn't even make it across to the other side.
I've also tried a few other ways to get the file into a byte-array, as well as encoding the file (base64), with no luck. For whatever reason, the call is successful and data gets transferred, but each time, when we try to open the image on the server side, it's corrupt and/or won't open as a JPG. I've tried small and large image files.
I have definitely done research and tried many solutions I have found, but nothing seems to be working. I'm sure I'm missing something obvious here, but can anyone steer me in the right direction on this?
Any help would be GREATLY appreciated!!
The answer to this was fairly simple. As it turns out, I did need to base64-encode the image, as it was simply getting corrupt somehow when sending to the service. I did this via this code:
String encodedPhotoStr = Base64.encodeToString(byteArrayPhoto, Base64.DEFAULT);
params.put("imagedata", encodedPhotoStr);
But it was vital on the PHP side to have a line added in order to decode this data as follows:
$raw_data_str = base64_decode($data_str_from_java)
This fixed the issue and the image is now viewable after being sent. So for whatever reason, sending raw binary/image data from java into the PHP just would not work (despite the curl script working fine), and I absolutely had to encode and decode it this way to get it to work from the Java code.

Streaming large files with play framework and third party API

I'm writing a play 2 application and I am struggling with a file streaming problem.
I retrieve my files using a third party API with a method having the following signature:
FileMetadata getFile(OutputStream destination, String fileId)
In a traditional Servlet application, if I wanted to send the content to my client I would have done something like:
HttpServletResponse resp;
myService.getFile(resp.getOutpuStream, fileId);
My problem is that in my play 2 Controller class I don't have access to the underlying OuputStream, so the simplest implementation of my controller method would be:
public static downloadFile(String id) {
ByteArrayOutputStream baos = new BAOS(...);
myApi.getFile(baos,id); //Load inside temp Array
ByteArrayInputStream bais = new BAIS(baos.toByteArray())
return Ok(bais);
}
It will work but it requires to load the whole content into memory before serving it so it's not an option (files can be huge).
I was thinking of a solution consisting in:
Defining a ByteArrayOutputStream (baos) inside my controller
Calling the third party API with this baos in parameter
Using the chunk return of the play framework to send the content of
the baos as soon as something is written inside by the 3rd party API
Problem is that I don't know if it possible (call to getFile is blocking so it would require multiple threads with a shared OutputStream) nor if it's overkill.
As someone ever faced this kind of problem and found a solution?
Could my proposed solution solve my problem?
Any insights will be appreciated.
Thanks
EDIT 1
Based on kheraud suggestion I have managed to have a working, but still not perfect, solution (code below).
Unfortunately if a problem occurs during the call to the getFile method, error is not sent back to the client (because I returned Ok) and the browser waits indefinitely for a file that will never come.
Is there a way to handle this case ?
public static Result downloadFile(String fileId {
Thread readerThread = null;
try {
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
//Reading must be done in another thread
readerThread = new DownloadFileWorker(fileId,pos);
readerThread.start();
return ok(pis);
} catch (Exception ex) {
ex.printStackTrace();
return internalServerError(ex.toString());
}
}
static class DownloadFileWorker extends Thread{
String fileId;
PipedOutputStream pos;
public DownloadFileWorker(String fileId, PipedOutputStream pos) {
super();
this.fileId = fileId
this.pos = pos;
}
public void run(){
try {
myApi.getFile(pos,fileId);
pos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
EDIT 2
I found a way to avoid infinite loading of the page by simply adding a pos.close in the catch() part of the worker thread. Client ends up with a zero KB file but I guess that's better than an infinite waiting.
There is something in the Play2 Scala framework made for that : Enumerators. This is very close to what you are thinking about.
You should have a look at this doc page for details
I didn't find something similar in the Play2 Java API, but looking in the fw code source, you have a :
public static Results.Status ok(java.io.InputStream content, int chunkSize)
method which seams to be what you are looking for. The implementation can be found in play.mvc.Results and play.core.j.JavaResults classes.
On the Play! mailing list, there recently was a discussion on the same topic:
https://groups.google.com/forum/#!topic/play-framework/YunJzgxPKsU/discussion
It includes a small snippet that allows non-scala-literates (like myself) use the scala streaming interface of Play!.

Convert byte[] to Base64 string for data URI

I know this has probably been asked 10000 times, however, I can't seem to find a straight answer to the question.
I have a LOB stored in my db that represents an image; I am getting that image from the DB and I would like to show it on a web page via the HTML IMG tag. This isn't my preferred solution, but it's a stop-gap implementation until I can find a better solution.
I'm trying to convert the byte[] to Base64 using the Apache Commons Codec in the following way:
String base64String = Base64.encodeBase64String({my byte[]});
Then, I am trying to show my image on my page like this:
<img src="data:image/jpg;base64,{base64String from above}"/>
It's displaying the browser's default "I cannot find this image", image.
Does anyone have any ideas?
Thanks.
I used this and it worked fine (contrary to the accepted answer, which uses a format not recommended for this scenario):
StringBuilder sb = new StringBuilder();
sb.append("data:image/png;base64,");
sb.append(StringUtils.newStringUtf8(Base64.encodeBase64(imageByteArray, false)));
contourChart = sb.toString();
According to the official documentation Base64.encodeBase64URLSafeString(byte[] binaryData) should be what you're looking for.
Also mime type for JPG is image/jpeg.
That's the correct syntax. It might be that your web browser does not support the data URI scheme. See Which browsers support data URIs and since which version?
Also, the JPEG MIME type is image/jpeg.
You may also want to consider streaming the images out to the browser rather than encoding them on the page itself.
Here's an example of streaming an image contained in a file out to the browser via a servlet, which could easily be adopted to stream the contents of your BLOB, rather than a file:
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
ServletOutputStream sos = resp.getOutputStream();
try {
final String someImageName = req.getParameter(someKey);
// encode the image path and write the resulting path to the response
File imgFile = new File(someImageName);
writeResponse(resp, sos, imgFile);
}
catch (URISyntaxException e) {
throw new ServletException(e);
}
finally {
sos.close();
}
}
private void writeResponse(HttpServletResponse resp, OutputStream out, File file)
throws URISyntaxException, FileNotFoundException, IOException
{
// Get the MIME type of the file
String mimeType = getServletContext().getMimeType(file.getAbsolutePath());
if (mimeType == null) {
log.warn("Could not get MIME type of file: " + file.getAbsolutePath());
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
resp.setContentType(mimeType);
resp.setContentLength((int)file.length());
writeToFile(out, file);
}
private void writeToFile(OutputStream out, File file)
throws FileNotFoundException, IOException
{
final int BUF_SIZE = 8192;
// write the contents of the file to the output stream
FileInputStream in = new FileInputStream(file);
try {
byte[] buf = new byte[BUF_SIZE];
for (int count = 0; (count = in.read(buf)) >= 0;) {
out.write(buf, 0, count);
}
}
finally {
in.close();
}
}
If you don't want to stream from a servlet, then save the file to a directory in the webroot and then create the src pointing to that location. That way the web server does the work of serving the file. If you are feeling particularly clever, you can check for an existing file by timestamp/inode/crc32 and only write it out if it has changed in the DB which can give you a performance boost. This file method also will automatically support ETag and if-modified-since headers so that the browser can cache the file properly.

Categories