Send image within Json to a webservice in Android - android

I want to send a json object from my android device to the server (in post).
In my json object, I need to add an image in Base64. I cannot use to String to convert my image to Base64 because a String is too short to contain a Base64 encoded file.
I must use a BytesArray.
How to send something like that to a JSON webservice ?
{
"emergency":"gsjqsbl",
"cod_valid":"O",
"image":{
"content":"/9j/4AAQSkZJRg ## MY VERY VERY VERY VERY VERY VERY VERY LONG IMAGE IN BASE64 ## BWNn+SV5H8KQADnn9ysrKA1EVX+lNDkSJ8ai8UADCLoAR3TWVlHT95AVvcfwCvD4Hq1joP5NX3Ciat7zyP4VlZW9bnl1sf//Z",
"content_type":"image/jpg"
},
"indoor":"yes",
"access_conditional":"text",
"geo_shape":{
"type":"Point",
"coordinates":[
2.0202024,
45.799005
]
},
"lastupdate":"",
"ref_fr_sdis91":"",
"name":"TEST IN UPPERCASE WITH SPECIAL CHARACTERS ;/*-é#$~æ€",
"geo_point_2d":"45.799005,2.0202024",
"source":"soures",
"objectid":"",
"aed_location":"Spopopo"
}
I really cannot use String.
Thanks
EDIT : What i've done yet :
//output stream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//write text
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write("json start { blabla: value");
//Write image
AssetManager assetManager = context.getAssets();
InputStream istr;
Bitmap bitmap = null;
try {
istr = assetManager.open("pictures/defib12.jpg");
bitmap = BitmapFactory.decodeStream(istr);
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos);
outputStream.write(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
//write text
OutputStreamWriter outputStreamWriter2 = new OutputStreamWriter(outputStream);
bufferedWriter = new BufferedWriter(outputStreamWriter2);
bufferedWriter.write("json end }");
HttpResponse response = restClient.executePostRequest(pushUrl, outputStream);
And :
public HttpResponse executePostRequest(String url, ByteArrayOutputStream outputStream) throws IOException {
LogWrapper.debug(DmaRestClient.class, "New HttpPost request: " + url);
DefaultHttpClient client = new DefaultHttpClient();
HttpPost request = new HttpPost(url);
request.setEntity(new ByteArrayEntity(outputStream.toByteArray()));
request.setHeader("Content-Type", "application/json");
return client.execute(request);
}

You should use a lib to do that. Check Genson, it provides two mechanisms to ser/de binary content: using the classic base64 encoded strings (but in your case it looks like it won't work) or ser/de as an array of ints. Note that you will have to use the same mechanism on the server side - but it could be another library that support this kind of format.
With Genson you would create classes to represent your json and image.content value would be the byte array. To enable this option in Genson:
Genson genson = new GensonBuilder().useByteAsInt(true).create();
genson.serialize(yourObject, theOutputStream);
If you want to do it by hand without using any lib you still can (but it is a bad practice...) use the same trick => Ser/de the byte array as an int array.

Related

Sending/receiving .wav file from Android app to Django server using JSON

I am trying to send a .wav file from my Android app to a Django server. The main problem is that on the server side constantly get this error: wave.Error: file does not start with RIFF id
From the Client point of view, this is the way I convert the test_audio.wav file to byte[]
HashMap<String, String> postParams = new HashMap<>();
InputStream inStream = testPronunciationView.getContext().getResources().openRawResource(R.raw.test_audio);
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(inStream);
int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
out.flush();
byte[] fileAudioByte = out.toByteArray();
// two options to transform in a string
// 1st option
String decoded = new String(fileAudioByte, "UTF-8");
// 2nd option
String decoded = toJSON(fileAudioByte);
// decoded = either one of above
postDataParams.put("Audio", decoded)
// ....
// prepare a POST request here to send to the server
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
writer.write(getPostDataString(postDataParams));
writer.flush();
writer.close();
os.close();
EDIT: Method to create the JSON string:
public static String toJSON(Object object) throws JSONException, IllegalAccessException
{
String str = "";
Class c = object.getClass();
JSONObject jsonObject = new JSONObject();
for (Field field : c.getDeclaredFields()) {
field.setAccessible(true);
String name = field.getName();
String value = String.valueOf(field.get(object));
jsonObject.put(name, value);
}
System.out.println(jsonObject.toString());
return jsonObject.toString();
}
On the server side I do:
audiofile_string = data['FileAudio']
audiofile_byte = list(bytearray(audiofile_string, 'utf8'))
temp_audiofile = tempfile.NamedTemporaryFile(suffix='.wav')
with open(temp_audiofile.name, 'wb') as output:
output.write(''.join(str(v) for v in audiofile_byte))
# The following line throws the error
f = wave.open(temp_audiofile.name, 'r') # wave.py library
So I think that I am doing something wrong in the conversion or in the post call. Any suggestion ? Thanks
Is there a specific reason you are trying to do this using JSON? You can't just stuff binary data into a JSON string.
If you can avoid using JSON, then just POST the binary data over HTTP using a multipart/form-data request.
If for some reason you are stuck on using JSON, you can use base64 encoding to achieve this. In your Android app, you need to base64 encode the binary data. This will result in a string. You can then send this string in your JSON to the server. On the server side, you will then need to get this base64 encoded string from the JSON, base64 decode, and then save it to file (or whatever you want to do with the binary data). Here's some small examples.
client side:
int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
out.flush();
byte[] fileAudioByte = out.toByteArray();
String encodedString = Base64.encodeToString(fileAudioByte, Base64.DEFAULT);
encodedString is a String that you will then add to your JSON to send to the server.
server side:
import base64
...
audiofile_string = data['FileAudio']
audiofile_byte= base64.b64decode(audiofile_string)
# audiofile_byte now contains the bytes of the audio file, proceed to write to disk

Android - Upload coded image (1 - 2 MB) to base64 to the server HTTP POST - JSON

How can I send large image/photo to the server using HTTP POST and JSON? I tried several methods but all methods wasn´t good (OutOfMemory Exceptions etc.).
"Classic" code:
Bitmap image;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.PNG, 100, stream);
image.recycle();
image = null;
byte[] byteArray = stream.toByteArray();
try {
stream.close();
} catch (IOException e1) {
}
stream = null;
String encoded = Base64.encodeToString(byteArray,
Base64.DEFAULT);
HttpClient httpClient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(Globals.URL + "/storageUploadFile");
httppost.setHeader("Token", Globals.Token);
String msg = "";
try {
JSONObject jo = new JSONObject();
jo.put("fileName", fileName);
jo.put("content", encoded);
httppost.setEntity(new StringEntity(jo.toString());
HttpResponse response = httpClient.execute(httppost);
HttpEntity httpentity = response.getEntity();
msg = EntityUtils.toString(httpentity);
//...
In this code I get exception here: httppost.setEntity(new StringEntity(jo.toString());
Image is saved on storage card. What do you recommend to upload the image? Send image chunk by chunk? I rather send it as one "item". I hope 2 MB is not so large. My API has parameter "content" and it´s the image in base64 encoding. Is it good way to transfer image as base64?
If you really need json and if you really need base64, you need to stream it instead of keeping all transformations in memory. If your image is 2Mb, in your method, you use:
2MB for the bytes
4.6MB for the base64 String (java strings are internally represented chars, which are 16bits)
4.6MB for the JSONObject.toString result in the String entity
That's a grand total of more than 11MB for just a simple 2MB image.
First step is to use a Json streaming API (I use Jackson)
Like so:
// The original size prevents automatic resizing which would take twice the memory
ByteArrayOutputStream baos = new ByteArrayOutputStream(byteArray.length * 1.2);
JsonGenerator jo = new JsonFactory().createGenerator(baos);
jo.writeStartObject();
jo.writeStringField("fileName", fileName);
// Jackson takes care of the base64 encoding for us
jo.writeBinaryField("content", byteArray);
jo.writeEndObject();
httppost.setEntity(new ByteArrayEntity(baos.toByteArray());
In this case, we only hold in memory byteArray and baos, with its underlying byte[] for a theoretical total of 2MB + 1.2*2MB = 4.4MB (No string representation is used, only 1 intermediate byte[]). Note that the base64 streaming to the byte[] is done transparently by Jackson.
If you still have memory issues (if you are going to send a 10MB image, for instance), you need to stream the content directly to the connection. For that, you could use HttpUrlConnection and use the connection.getOutputStream() as a parameter to createGenerator.

How to post an image to Twitter using statuses/update_with_media in android

I need to post a image to Twitter. I have integrated Twitter in my app. I need to tweet the image as such not as an URL link. I don't want to use TwitPic.
I used the following code to create the multipart entity. It give 404 error.
Bitmap bm = null;
String encodedImage = "";
try {
URL aURL = new URL("http://50.57.227.117/blacksheep/uploaded/Detailed_images/961314275649aladdins.jpg");
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is, 8192);
bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
imageBytes = baos.toByteArray();
encodedImage = Base64.encodeToString(imageBytes, Base64.DEFAULT);
Log.v("encodedImage >>",encodedImage);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
HttpClient httpClient = new DefaultHttpClient();
HttpPost postRequest = new HttpPost(
"https://api.twitter.com/1.1/statuses/update_with_media.json");
ByteArrayBody bab = new ByteArrayBody(imageBytes, "forest.jpg");
MultipartEntity reqEntity = new MultipartEntity(
HttpMultipartMode.BROWSER_COMPATIBLE);
reqEntity.addPart("media", bab);
reqEntity.addPart("status", new StringBody("test image"));
postRequest.setEntity(reqEntity);
HttpResponse response = httpClient.execute(postRequest);
BufferedReader reader = new BufferedReader(new InputStreamReader(
response.getEntity().getContent(), "UTF-8"));
String sResponse;
StringBuilder s = new StringBuilder();
while ((sResponse = reader.readLine()) != null) {
s = s.append(sResponse);
}
System.out.println("Response: " + s);
// Update status
//twitter4j.Status response = twitter.updateStatus(encodedImage);
// twitter4j.Status response1 = twitter.updateStatus(status);
//Log.d("Status", "> " + response1.getText());
} catch (TwitterException e) {
// Error in updating status
Log.d("Twitter Update Error", e.getMessage());
}
First attempt:
So it seemed like you were using Apache's old http client to make a request external to the library you were using to help integrate with twitter, twitter4j. I assumed you were using a version prior to 3.03 which is latest, and didn't want you to upgrade. You see, update_with_media is quite new, so I didn't think your version had implemented it.
The problem with what you were doing is that twitter uses oauth for authentication. So you'd need to "sign" a request with the access token you'd obtained. Twitter4j, AFAIK, does this for you. You can't use a seperate client to make some calls without reference to your nice helper library without breaking authentication.
The endpoint, ../update_with_media is defined to update a status for the currently authenticating user. I suspect that, since there was no access token and no user in your request, that endpoint doesn't even make sense, so twitter were interpreting it as a 404 (not found) rather than a 401 (unauthorized)- funny.
So the first attempt was not to require you to upgrade to twitter4j. It's a pain to upgrade sometimes! Instead, you can hack with the library as is detailed with this blog. But that wasn't easy as the libraries were different.
So, something else we could try, if you really wanted to make a seperate request to twitter4j, was to actually do the signing, perhaps using scribe to make it easier.... roughly:
final OAuthService myTwitterService = TwitterClient.getTwitterClient().getService();
final OAuthRequest aNiceOAuthRequest = new org.scribe.model.OAuthRequest(
YOURPOST, THATURL);
etc.
Second attempt:
But let's not do all this- turns out you had the latest version of twitter4j anyway. Sorry for going down a cul-de-sac first- I shouldn't have assumed, but I've included the above for help for anybody else should they need it.
It turns out the latest version has implemented this endpoint- documentation here. Except it takes a StatusUpdate object instead. So you want to do something like:
final StatusUpdate statusUpdate = new StatusUpdate("Hallee hallo my status java.lang.String here...");
// now do as you did until:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
imageBytes = baos.toByteArray();
encodedImage = Base64.encodeToString(imageBytes, Base64.DEFAULT);
// then flip the stream
byte[] myTwitterUploadBytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(myTwitterUploadBytes);
// doo and double check your encoding etc, similar to in your question..
statusUpdate.setMedia("give me a java.lang.String name", bis);
// then continue just using twitter for j- update status as you would
//... get twitter etc...
//
twitter4j.Status response = twitter.updateStatus(statusUpdate);
Haven't currently got a box on me to test- should be about right. If it still gives 404s, what is the error code in the response? Are you authenticated?
If that doesn't work, we can try some of the above too as a back up.
Hope this helps,
best,
Tom.
Using 4.0.3 (perhaps earlier) it's very simple to embed images in a tweet with twitter4j:
Twitter twtobj;
StatusUpdate stsupd;
Status stsres;
twtobj=twitterFactory.getInstance();
twtobj.setOAuthConsumer(csmkey,csmsec);
twtobj.setOAuthAccessToken(new AccessToken(acstkn,acssec));
stsupd=new StatusUpdate(msgtxt);
if(medurls.length>0) {
long[] medidns=new long[medurls.length];
for(int xa=0; xa<medurls.length; xa++) {
String medurl=Util.resolveRelativeUrl(medurls[xa]);
InputStream imgstm=null;
try {
imgstm=new URL(medurl).openConnection().getInputStream();
medidns[xa]=twtobj.uploadMedia(medurl,imgstm).getMediaId(); // this actually uploads the image to Twitter at this point
}
catch(MalformedURLException thr) { throw new ShfFail(Fail.IMAGE_URL ,"The media URL is not valid: " +medurl+" ("+thr.getMessage()+")"); }
catch(IOException thr) { throw new ShfFail(Fail.IMAGE_READ,"The media could not be read: "+medurl+" ("+thr.getMessage()+")"); }
finally { GenUtil.close(imgstm); }
}
stsupd.setMediaIds(medidns);
}
stsres=twtobj.updateStatus(stsupd);
Note that up to 4 images, or 1 animated GIF, or 1 video are allowed, as of 2015-06-10.
Note also that I am capturlng the image streams to close them explicitly in the outer block (not shown). This may be unnecessary, but I can't find positive confirmation of that.
If anyone cares, resolveRelativeUrls is a convenience to allow a relative path to be resolved as a file URL from the current folder:
static public String resolveRelativeUrl(String url) {
if(!TextUtil.stringCT(url,"://")) {
url=new File(url).getAbsoluteFile().toURI().toString();
}
return url;
}
The utility method stringCT is case-insensitive contains.

parse Multipart response in Android

I'm sending images and json text from the android client to a tomcat server and the other way around by using Multipart HttpPost's. Sending a Multipart Entity to the server is no big deal, because you can process the parts easily using request.getPart(<name>). But at the client side you can only access the response as a Stream. So I end up appending both, the JSON string and the image to the same ServletOutputStream and have to parse them by hand on the client side. I found apache-mime4j in the web but its hardly documented and I cant find a single example how to use it.
On the server side I build the response like this:
ServletResponse httpResponse = ctx.getResponse();
ResponseFacade rf = (ResponseFacade) httpResponse;
rf.addHeader("Access-Control-Allow-Origin", "*");
rf.addHeader("Access-Control-Allow-Methods", "POST");
rf.addHeader("content-type", "multipart/form-data");
httpResponse.setCharacterEncoding("UTF-8");
MultipartResponse multi = new MultipartResponse((HttpServletResponse) httpResponse);
ServletOutputStream out = httpResponse.getOutputStream();
multi.startResponse("text/plain");
out.println(CMD + "#" + content);
multi.endResponse();
multi.startResponse("image/jpeg");
out.write(data);
multi.endResponse();
multi.finish();
ctx.complete();
And on the client side on Android I want to access the text and the image data:
InputStream is = response.getEntity().getContent();
MimeStreamParser parser = new MimeStreamParser();
MultipartContentHandler con = new MultipartContentHandler();
parser.setContentHandler(con);
try {
parser.parse(is);
String json = con.getJSON(); //get extracted json string
byte[] imgBytes = con.getBytes(); //get extracted bytes
} catch (MimeException e) {
e.printStackTrace();
} finally {
is.close();
}
class MultipartContentHandler implements ContentHandler{
public void body(BodyDescriptor bd, InputStream in) throws MimeException, IOException {
//if MIME-Type is "text/plain"
// process json-part
//else
// process image-part
}
In the method body(BodyDescriptor bd, InputStream in) my whole response is treated as text\plain mime type. So I finally have to parse every byte manually again and the whole apache-mime4j is useless. Can you tell me what I am doing wrong? Thanks!
Ok i finally solved it myself. No here's what i did:
First I need to create a multipart/mixed Response at the server side. It can be done using apache-mime-4j API:
ServletResponse httpResponse = ctx.getResponse();
ResponseFacade rf = (ResponseFacade) httpResponse;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("multipart/mixed");
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, "SEPERATOR_STRING",Charset.forName("UTF-8"));
entity.addPart("json", new StringBody(CMD + "#" + content, "text/plain", Charset.forName("UTF-8")));
entity.addPart("image", new ByteArrayBody(data, "image/jpeg", "file"));
httpResponse.setContentLength((int) entity.getContentLength());
entity.writeTo(httpResponse.getOutputStream());
ctx.complete();
Now at the client side to access the MIME-Parts of the HttpResponse I use the javax.mail API.
ByteArrayDataSource ds = new ByteArrayDataSource(response.getEntity().getContent(), "multipart/mixed");
MimeMultipart multipart = new MimeMultipart(ds);
BodyPart jsonPart = multipart.getBodyPart(0);
BodyPart imagePart = multipart.getBodyPart(1);
But you can't use the native API, instead take this one http://code.google.com/p/javamail-android/
Now you can proceed handling your individual parts.
It is also possible with apache-mime-4j:
HttpURLConnection conn = ...;
final InputStream is = conn.getInputStream();
try {
final StringBuilder sb = new StringBuilder();
sb.append("MIME-Version: ").append(conn.getHeaderField("MIME-Version")).append("\r\n");
sb.append("Content-Type: ").append(conn.getHeaderField("Content-Type")).append("\r\n");
sb.append("\r\n");
parser.parse(new SequenceInputStream(new ByteArrayInputStream(sb.toString().getBytes("US-ASCII")), is));
} catch (final MimeException e) {
e.printStackTrace();
} finally {
is.close();
}

Save and Access the data through web service in remote server in android [duplicate]

I have an idea for an app and am currently learning Android development. I'm fairly familiar with creating simple standalone apps.
I'm also familiar with PHP and webhosting.
What I want to do is, make an android app send an image to a server via the internet and make the server return a processed image. I have no clue how I'd do that.
Can you please tell me how can I go about achieving this or which topics should I look into? Also, what scripts can I use to do the processing on the web server? Particularly, can I use PHP or Java?
Thanks!
For Image Uploading
///Method Communicate with webservice an return Yes if Image uploaded else NO
String executeMultipartPost(Bitmap bm,String image_name) {
String resp = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bm.compress(CompressFormat.JPEG, 75, bos);
byte[] data = bos.toByteArray();
HttpClient httpClient = new DefaultHttpClient();
HttpPost postRequest = new HttpPost("domain.com/upload_image.php");
ByteArrayBody bab = new ByteArrayBody(data, image_name);
MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
reqEntity.addPart("uploaded", bab);
reqEntity.addPart("photoCaption", new StringBody("sfsdfsdf"));
postRequest.setEntity(reqEntity);
HttpResponse response = httpClient.execute(postRequest);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
String sResponse;
StringBuilder s = new StringBuilder();
while ((sResponse = reader.readLine()) != null) {
s = s.append(sResponse);
}
resp=s.toString();
} catch (Exception e) {
// handle exception here
Log.e(e.getClass().getName(), e.getMessage());
}
return resp;
}
//PHP Code
<?php
$target = "upload/";
$target = $target . basename( $_FILES['uploaded']['name']) ;
$ok=1;
if(move_uploaded_file($_FILES['uploaded']['tmp_name'], $target))
{
echo "yes";
}
else {
echo "no";
}
?>
Normally we do it with http connection, you can pass the image in the
post params, for further reference please see the link
You have to create a simple php web service which accepts parameter as image bytes and which process the image and store in server. For this android app will send image data in bytes to the server using HttpPost.
For retrieving purpose you have to create a other web service which will output the file name of image from where android app can retrieve the image

Categories

Resources