Downloading big video file giving out of memory error - android

I have used intent service to download a set video files, but while downloading some big size videos it is giving out of memory error in this part "byte[] responseBody = client.execute(getMethod, responseHandler);", I know byte array exceeds the size allotted heap for an app.
I am looking for some alternative solution to overcome this issue, please suggest me if you have some good fix.
#Override
public void onHandleIntent(Intent i) {
Log.d("gl", "onhandleintent");
HttpGet getMethod = new HttpGet(i.getData().toString());
int result = Activity.RESULT_CANCELED;
ad_id =i.getStringExtra("ad_id");
try {
ResponseHandler<byte[]> responseHandler = new ByteArrayResponseHandler();
byte[] responseBody = client.execute(getMethod, responseHandler);
Log.d("gl", "file name " + i.getData().getLastPathSegment());
File output = new File(SharedPreferenceClass.readName(
Downloader.this, "videoFolder", "itaxi-videos"), i
.getData().getLastPathSegment());
if (output.exists()) {
output.delete();
}
FileOutputStream fos = new FileOutputStream(output.getPath());
fos.write(responseBody);
fos.close();
result = Activity.RESULT_OK;
} catch (IOException e2) {
Log.e(getClass().getName(), "Exception in download", e2);
result = Activity.RESULT_CANCELED;
}
Bundle extras = i.getExtras();
// sending back datas to the handle for further updations like db
if (extras != null) {
Messenger messenger = (Messenger) extras.get(EXTRA_MESSENGER);
Message msg = Message.obtain();
msg.arg1 = result;
try {
Bundle bundle = new Bundle();
bundle.putString("ad_id", ad_id);
msg.setData(bundle);
messenger.send(msg);
} catch (android.os.RemoteException e1) {
Log.w(getClass().getName(), "Exception sending message", e1);
}
}
}
Thanks in advance

The problem is this: You are using a ResponseHandler that simply reads the entire HTTP response into a byte array in memory. You don't have that much memory.
You need to get the underlying InputStream and loop, reading a reasonable amount of data from the stream then writing to your output file.
The InputStream is inside the HttpEntity which you get from the HttpResponse
You can either:
A) Write a custom ResponseHandler whose constructor you pass your File to and it does all the work.
or
B) Just call client.execute(getMethod). This will return the HttpResponse directly.

Related

Android Http Response is incomplete. Returns an unterminated json object

I am using HttpClient 4.3.6 to perform http GET and POST requests. Right now I am using multipartentity to send a few string parameters and an image in the form of a file. I am able to successfully post the data but my problem comes in when I get the HTTP response. The response contains json data.
What happens is the HTTP response is incomplete and when i try to create a json object with the data i get jsonexception error saying:
Unterminated object at character 407.
I noticed that the response does not contain closed braces. Is this a problem on android or should I check the server? Because I am able to see the data properly on postman and on ios. I have never faced this issue before and don't know how to solve this.
This is my code to post and get the response:
#Override
protected String doInBackground(String... params) {
try {
String url = params[0];
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
MultipartEntity entity = new MultipartEntity();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] imageBytes = baos.toByteArray();
ByteArrayBody bab = new ByteArrayBody(imageBytes, "image.jpg");
entity.addPart("image_data", bab);
entity.addPart("action", new StringBody("1", "text/plain", Charset.forName("UTF-8")));
entity.addPart("name", new StringBody("asdfg", "text/plain", Charset.forName("UTF-8")));
entity.addPart("user_id", new StringBody("157", "text/plain", Charset.forName("UTF-8")));
entity.addPart("birthday", new StringBody("18-04-1995", "text/plain", Charset.forName("UTF-8")));
entity.addPart("gender", new StringBody("male", "text/plain", Charset.forName("UTF-8")));
entity.addPart("is_jlpt_student", new StringBody(String.valueOf(0), "text/plain", Charset.forName("UTF-8")));
entity.addPart("relationship", new StringBody("Father", "text/plain", Charset.forName("UTF-8")));
entity.addPart("relationship_id", new StringBody(String.valueOf(10002), "text/plain", Charset.forName("UTF-8")));
entity.addPart("is_creator", new StringBody(String.valueOf(1), "text/plain", Charset.forName("UTF-8")));
entity.addPart("email", new StringBody(email, "text/plain", Charset.forName("UTF-8")));
httppost.setEntity(entity);
HttpResponse resp = httpclient.execute(httppost);
String response = EntityUtils.toString(resp.getEntity());
Log.i("HttpResponse", response);
return response;
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute (String result) {
super.onPostExecute(result);
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(result);
JSONObject json_data = jsonObject.getJSONObject("data");
String json_userid = json_data.getString("user_id");
String json_username = json_data.getString("name");
String json_email = json_data.getString("email");
String json_country = json_data.getString("country_code");
String json_imagefilename = json_data.getString("image_filename");
String json_imgurl = json_data.getString("image_url");
Toast.makeText(ParentGuardianProfile.this, "ImageFile " + json_imagefilename, Toast.LENGTH_SHORT).show();
User new_user = userdao.createUser(json_userid, json_username, json_email,json_imagefilename,json_country,selectedImageUri.toString(), 1);
Log.i("SQLITE", "added user : " + new_user.getmUserName() + new_user.getmId());
} catch (JSONException e) {
e.printStackTrace();
}
}
And my json response is :
{"status":1,"message":"success","data":{"child_id":"381","name":"asdfg","image_filename":"C201603021734476.jpg","image_url":"https:\/\/innokid.blob.core.windows.net\/media\/child\/381.jpg","birthday":"18-04-1995","gender":"male","is_jltp_student":"0","relationship":"Father","relationship_id":"10002","is_creator":1,"rank":1,"qrcode_url":"http:\/\/innokid.azurewebsites.net\/uploads\/qrcode\/child_381.png"
I tried using String buffer as suggested in this post String is being truncated when its too long . But i still get the same result.
Code looks ok at first glance.
How do you got know that the json data is cut? Logcat can truncate text. Debugger should be more reliable in this case.
Try to generate this same request with some tools like curl / SoapUI and validate JSON you got with some formatter / validator (you'll easily find a few of such tools).
It's beyond the range of question, but using raw Android built-in communication libraries seems to be a little bit masochistic. Have you ever consider to use Retrofit?
I think this code is problematic String response = EntityUtils.toString(resp.getEntity());
may be you should use some other function to convert response toString...
Apparently the json is missing two curly brackets '}}' at the end, which can happen due to some bug in the toString code.
I pulled up an old project that was using the org.apache.http stuff and below is how I was parsing the response. As you can see it is rather cumbersome. There are many tested and maintained libraries out there that are better suited to this kind of heavy-lifting.
// Get hold of the response entity (-> the data):
HttpEntity entity = response.getEntity();
if (entity != null) {
// Read the content stream
InputStream instream = entity.getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
instream = new GZIPInputStream(instream);
}
// Convert content stream to a String
resultString = convertStreamToString(instream);
instream.close();
// Do stuff with resultString here
// Consume Content
entity.consumeContent();
}
And the convertStreamToString() method:
private static String convertStreamToString(InputStream is) {
/*
* To convert the InputStream to String we use the
* BufferedReader.readLine() method. We iterate until the BufferedReader
* return null which means there's no more data to read. Each line will
* appended to a StringBuilder and returned as String.
*
* (c) public domain:
* http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/
* 11/a-simple-restful-client-at-android/
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8192);
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
I finally solved this issue by replacing httpclient library with Android Asynchronous Http Client. Now it works fine. Thanks a lot for your help!
However, I still dont understand why the response was truncated when i used httpclient.

How to fix "400 bad request" with uploading file on android with java HttpClient?

I need to upload a file to server. If i use the "curl -i -F filedata=#"PATH TO FILE" http://█.199.166.14/audiostream " it return a 200 OK code (Or may be this command incorrect) .
But when I use java function
public String send()
{
try {
url = "http://█.199.166.14/audiostream";
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "test.pcm");
try {
Log.d("transmission", "started");
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
ResponseHandler Rh = new BasicResponseHandler();
InputStreamEntity reqEntity = new InputStreamEntity(new FileInputStream(file), -1);
reqEntity.setContentType("binary/octet-stream");
reqEntity.setChunked(true); // Send in multiple parts if needed
httppost.setEntity(reqEntity);
HttpResponse response = httpclient.execute(httppost);
response.getEntity().getContentLength();
StringBuilder sb = new StringBuilder();
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(response.getEntity().getContent()), 65728);
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
}
catch (IOException e) { e.printStackTrace(); }
catch (Exception e) { e.printStackTrace(); }
Log.d("Response", sb.toString());
Log.d("Response", "StatusLine : " + response.getStatusLine() + " Entity: " + response.getEntity()+ " Locate: " + response.getLocale() + " " + Rh);
return sb.toString();
} catch (Exception e) {
// show error
Log.d ("Error", e.toString());
return e.toString();
}
}
catch (Exception e)
{
Log.d ("Error", e.toString());
return e.toString();
}
}
It's return 400 Bad request.
I'm also not sure that server proceed correctly my attempts to upload this file, but I can't check it.
From the error received its likely a bad formatted HTTP query. If audiostream is a php, write the full link.
Also it seems that there might be a wrong/bad encoded char at "http://█.199.166.14/audiostream, the link should be http://(IP or DNS)/(rest of URL)(the URI)
You should erase the link, then manually writte it again.
If those didnt fix the issue, its also possible that the Server (or its path equipment) might be blocking you. Check from the Access Log and the security rules of its accesses, that you are not blocked (some routers may block users from performing repeated querys as a sort of anti "Denial of Service" measure)

Android: Send serialized objects interspersed with parameters to server

Situation: : I must either send a parameter to the server, or a serialized object. It can be in any random order.
What I've done: As per this post, I developed a protocol to let the server know what I'm sending just before I actually send it. If its any string, it just goes via PrintWriter.println().
But just before sending a serialized object via ObjectInputStream, I send a PrintWriter.println("O"). So the server knows it must expect a serialized object next.
Problem: I'm getting the StreamCorruptedException, even though I'm using only one instance of ObjectInputStream at the client throughout the socket's life. Should anything be done at the Server end ?
----------------------- EDITED BELOW ---------------------
Client End:
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(thisSocket.getOutputStream())),
true);
if (!sent){
out.println("Dummy");
sent = true;
}
objectOutputStream.flush();
objectOutputStream.reset();
out.println("#SerialO#"); //This param signals the server
//that an object will be sent next.
if(((calcSum) this.obj).getRemotable()){
/*objectOutputStream.flush();
objectOutputStream.reset();*/
Log.i("ENDPOINT", "Sending Serialized data ...");
objectOutputStream.writeObject(this.obj);
objectOutputStream.flush();
objectOutputStream.reset();
// Get the byte array
byte[] byteArray = byteArrayOutputStream.toByteArray();
}
Server End:
while (!Thread.currentThread().isInterrupted()) {
try{
this.input = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
// NOW DECLARED AND USED IN UpdateUIThread
//PrintStream out = new PrintStream(socket.getOutputStream());
if(!sent)
{
flg = this.input.readLine().toString();
Log.i("SERVER","SERVER Received False flg as "+flg);
sent = true;
}
else{
flg = this.input.readLine().toString();
System.out.println("SERVER Received flg as "+flg);
if(flg.contains("#SerialO#")){ //Be prepared to
// receive a serilized obj from client
//Log.d("SERVER","Received an object");
CommunicationThread commThread = new CommunicationThread(this.clientSocket,it);
new Thread(commThread).start();
}
else
{
//Log.d("SERVER","Received a parameter "+flg);
executor = Executors.newFixedThreadPool(5);
executor.execute(new updateUIThread(this.clientSocket,flg));
}
}
} catch (IOException e) {
e.printStackTrace();
Log.e("SERVER", "Creation went bad -- "+e.toString());
break;
}
catch (Exception e)
{
Log.d("CLIENT TAG", "Creation went bad -- "+e.toString());
}
}
}
...
public CommunicationThread(Socket clientSocket, Intent it) {
this.clientSocket = clientSocket;
try {
Log.d("SERVER","Inside Server's Comm Thread");
if (mInput==null){
Log.i("SERVER","Receiving very first serialized obj");
mOutput = new ObjectOutputStream(this.clientSocket.getOutputStream());
System.out.println("Inside Server's Comm Thread 2 ");
// EXCEPTION OCCURS IN THE LINE BELOW.
mInput = new ObjectInputStream(new BufferedInputStream(this.clientSocket.getInputStream()));
}
You can't mix streams and readers/writers on the same socket if any of them is buffered. In this situation I would use the object stream for everything.

how to send Image to server with content length and image data as body?

I am developing one android application and in my application i want to send image data to my server. So I am using put request. I dont want to send image data with json object. I just want to send image data as body of HTTP put request. And also want to set header content length as well. So I tried it in following ways.
#Override
protected Void doInBackground(Void... unused) {
HttpClient hc = new DefaultHttpClient();
String message;
HttpPut p = new HttpPut("abc.com");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.JPEG, 100, bos);
byte[] data = bos.toByteArray();
try {
p.setEntity(new StringEntity(data.toString()));
p.setHeader("Content-Length", Long.toString(data.length));
//p.setHeader("Content-Length", "0");
HttpResponse resp = hc.execute(p);
if (resp != null)
{
if (resp.getStatusLine().getStatusCode() == 204)
{
}
}
Log.d("Status line", "" + resp.getStatusLine().getStatusCode());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onProgressUpdate(Void... unsued) {
}
#Override
protected void onPostExecute(Void result) {
try {
if (dialog.isShowing())
dialog.dismiss();
Log.i("#############################", "before exception "+result);
} catch (Exception e) {
Log.i("#############################", "inside catch ");
Toast.makeText(getApplicationContext(),"Error"+e,
Toast.LENGTH_LONG).show();
Log.e(e.getClass().getName(), e.getMessage(), e);
}
}
}
So I have two uncleared questions in my mind.
1) I want to send image data to my server without using multipart and without using json object. Just set it into body of request. How to do that?
2) I want to set content length into header part. How to do that?
When I tried to set content length it gives me system error client protocol exception
Need help regarding these two questions. thank you.
As one of the commenter stated above, you don't want to change your byte[] into a String. You will most likely corrupt the image you're trying to send (I tried myself).
You should try using ByteArrayEntity instead (as opposed to StringEntity):
p.setEntity(new ByteArrayEntity(data));

Sending Large file over network/localhost

Here's the code:
private void sendFile(InputStream file, OutputStream out) throws IOException {
Log.d(TAG, "trying to send file...");
final int buffer_size = 4096;
try {
byte[] bytes = new byte[buffer_size];
while(true) {
int count = file.read(bytes, 0, buffer_size);
if (count == -1) {
break;
}
out.write(bytes, 0, count);
Log.d("copystream", bytes + "");
}
} catch (Exception e) {
Log.e("copystream", "exception caught while sending file... " + e.getMessage());
}
}
I'm trying to send a large File (InputStream file) over an output stream (OutputStream out). This code works for smaller files, but for something like 5mb and above (I haven't benchmarked the limit), it just freezes after sometime without error or anything.
Log.d("copystream", bytes + ""); would output for some time, but will eventually stop logging.
Log.e("copystream", "exception caught while sending file... " + e.getMessage()); never shows.
This is part of a larger codebase which is actually a file server that runs on the Android device.
Any ideas?
Here's what made it work:
while (true) {
synchronized (buffer) {
int amountRead = file.read(buffer);
if (amountRead == -1) {
break;
}
out.write(buffer, 0, amountRead);
}
}
Use Multipart POST. Something like
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null,null);
entity.addPart("File", new FileBody (new File(FILE_PATH), MIME_TYPE));
httppost.setEntity(entity);
HttpResponse response = httpclient.execute(httppost);
return response;
Use AsyncTask Class for this, here is link for example
http://developer.android.com/reference/android/os/AsyncTask.html

Categories

Resources