Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-15594] Android: base64 encode of a large file fails

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2014-05-23T22:01:17.000+0000
Affected Version/sRelease 3.2.0, Release 3.2.1
Fix Version/s2014 Sprint 08, 2014 Sprint 08 SDK, Release 3.3.0, Release 3.4.0
ComponentsAndroid
LabelssupportTeam
ReporterDavide Cassenti
AssigneeSunila
Created2013-10-28T11:12:13.000+0000
Updated2014-07-31T21:04:17.000+0000

Description

Problem description

Trying to use base64encode with a very large file, fails with an 'out of memory' error.

Use case

The use case would be to upload a base64-encoded file using an HTTPClient. The conversion to base64 fails.

Sample code

var filename = "SOME_LARGE_FILE";
var file = Titanium.Filesystem.getFile(filename);

var toUpload = file.read();
var ws = {
  file_name: filename,
  data: Ti.Utils.base64encode(toUpload)
};

var xhr = Ti.Network.createHTTPClient({
  onload: function(e) {
    Ti.API.info(this.responseText);
  }
});
xhr.open('POST', 'SOME_UPLOAD_URL');
xhr.send(ws);

Attachments

FileDateSize
movie.mp42014-05-13T18:02:05.000+00002549211

Comments

  1. Sunila 2014-01-27

    Modified the code to use the stream directly in the HTTPClient to avoid memory allocation. TiBlob is modified to support stream and take file as input. https://github.com/appcelerator/titanium_mobile/pull/5261
  2. Sunila 2014-01-27

    Modified the code to use the stream directly in the HTTPClient to avoid memory allocation. TiBlob is modified to support stream and take file as input. https://github.com/appcelerator/titanium_mobile/pull/5261
  3. Sunila 2014-01-27

    Run the following code to reproduce the issue. After merging the fix, set the the 'tryTheFix' to true and run the sample again to see the fix
       	var win = Ti.UI.createWindow();
       	win.open();
       	  
       	  
       	var percent_done = 0;
       	  
       	var xhr = Ti.Network.createHTTPClient({
       	     onload : function(e) {
       	    	 Ti.API.info("onLoad:"+this.responseText);
       	       
       	     },
       	     onerror : function(e) {
       	    	 Ti.API.info("e.error: "+e.error);
       	     },
       	     onreadystatechange: function(e){
       	    	 Ti.API.info("onreadystatechange: "+this.readyState);  
       	     },
       	     timeout: 10000
       	});
       	  
       	  
       	function upload(){
       	    console.log("STARTING UPLOAD");
       	    var filename = "Wildlife.wmv";
       	    var url = "http://www.w3schools.com/ajax/demo_post.asp";    //using this server for simplicity's sake
       	    var file = Ti.Filesystem.getFile(Ti.Filesystem.getResourcesDirectory()+filename); //just use any large-ish file, 500kb+ or so
       	    xhr.open("POST", url);
       	    // set 'tryTheFix' to true to see the fix
       	    var tryTheFix = false;
       	    var ws;
       	    if (tryTheFix) {
       	        ws = {
       	            file_name: filename,
       	            data: Ti.Utils.base64encode(file)
       	        };
       	    } else {
       	        var toUpload = file.read();
       	        ws = {
       	            file_name: filename,
       	            data: Ti.Utils.base64encode(toUpload)
       	        };
       	    }
       	    xhr.send(ws);       
       	}    
       	  
       	upload();
       
  4. Lokesh Choudhary 2014-05-08

    Reopening the ticket.

    1. When tryTheFix = false:

    We get the following errors:
       [ERROR] :  TiHttpClient: (TiHttpClient-1) [3259,3259] HTTP Error (java.net.SocketException): sendto failed: EPIPE (Broken pipe)
       [ERROR] :  TiHttpClient: java.net.SocketException: sendto failed: EPIPE (Broken pipe)
       [ERROR] :  TiHttpClient: 	at libcore.io.IoBridge.maybeThrowAfterSendto(IoBridge.java:499)
       [ERROR] :  TiHttpClient: 	at libcore.io.IoBridge.sendto(IoBridge.java:468)
       [ERROR] :  TiHttpClient: 	at java.net.PlainSocketImpl.write(PlainSocketImpl.java:507)
       [ERROR] :  TiHttpClient: 	at java.net.PlainSocketImpl.access$100(PlainSocketImpl.java:46)
       [ERROR] :  TiHttpClient: 	at java.net.PlainSocketImpl$PlainSocketOutputStream.write(PlainSocketImpl.java:269)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.io.AbstractSessionOutputBuffer.flushBuffer(AbstractSessionOutputBuffer.java:87)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.io.AbstractSessionOutputBuffer.flush(AbstractSessionOutputBuffer.java:94)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.AbstractHttpClientConnection.doFlush(AbstractHttpClientConnection.java:169)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.SocketHttpClientConnection.close(SocketHttpClientConnection.java:192)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.conn.DefaultClientConnection.close(DefaultClientConnection.java:161)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.conn.AbstractPooledConnAdapter.close(AbstractPooledConnAdapter.java:158)
       [ERROR] :  TiHttpClient: 	at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:428)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:653)
       [ERROR] :  TiHttpClient: 	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:637)
       [ERROR] :  TiHttpClient: 	at ti.modules.titanium.network.TiHTTPClient$ClientRunnable.run(TiHTTPClient.java:1291)
       [ERROR] :  TiHttpClient: 	at java.lang.Thread.run(Thread.java:841)
       [ERROR] :  TiHttpClient: Caused by: libcore.io.ErrnoException: sendto failed: EPIPE (Broken pipe)
       [ERROR] :  TiHttpClient: 	at libcore.io.Posix.sendtoBytes(Native Method)
       [ERROR] :  TiHttpClient: 	at libcore.io.Posix.sendto(Posix.java:156)
       [ERROR] :  TiHttpClient: 	at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:177)
       [ERROR] :  TiHttpClient: 	at libcore.io.IoBridge.sendto(IoBridge.java:466)
       [ERROR] :  TiHttpClient: 	... 16 more
       

    2. When tryTheFix = true:

    We see these logs:
       [ERROR] :  TiHttpClient: (KrollRuntimeThread) [72,72] Error adding post data (data): bad base-64
       [INFO] :   onreadystatechange: 1
       [DEBUG] :  OpenGLRenderer: Enabling debug mode 0
       [TRACE] :  RenderScript: Application requested CPU execution
       [TRACE] :  RenderScript: 0x79e180b0 Launching thread(s), CPUs 4
       [DEBUG] :  Window: Checkpoint: postWindowCreated()
       [INFO] :   onreadystatechange: 3
       [INFO] :   onreadystatechange: 3
       [DEBUG] :  skia: --- SkImageDecoder::Factory returned null
       [DEBUG] :  skia: --- SkImageDecoder::Factory returned null
       [INFO] :   onreadystatechange: 4
       [INFO] :   onLoad:<p>This content was requested using the POST method.</p>
       [INFO] :   <p>Requested at: 5/8/2014 1:46:25 PM</p>
       [DEBUG] :  HTTPClient: The persistent handle is disposed.
       
  5. Sunila 2014-05-12

    Lokesh, The error you get with 1 may be because the server is terminating the connection because it is taking too long. Can you attach or upload the file you tried in some place? I am looking in to 2.
  6. Lokesh Choudhary 2014-05-12

    Hi [~sunila], The link for the attachment is : https://www.dropbox.com/s/2wt6yo16i1lto2n/wildlife.wmv
  7. Sunila 2014-05-13

    New PR submitted https://github.com/appcelerator/titanium_mobile/pull/5677
  8. Sunila 2014-05-16

    To avoid getting Broken pipe error, you could setup a local server to accept the file upload. Here is the PHP script to try
       <?php
       set_time_limit(0);
       $destination_path ="temp/"; 
       $target_path = "../htdocs/" . $destination_path;
       $target_path = $target_path . basename( $_FILES['data']['name']); 
       
       echo "Source=" .        $_FILES['data']['name'] . "<br />"; 
       echo "Destination=" .   $destination_path . "<br />"; 
       echo "Target path=" .   $target_path . "<br />"; 
       echo "Size=" .          $_FILES['data']['size'] . "<br />"; 
       
       
       if(move_uploaded_file($_FILES['data']['tmp_name'], $target_path)) {
           echo "The file ".  basename( $_FILES['data']['name']). 
           " has been uploaded";
       } else{
           echo "There was an error uploading the file, please try again!";
       }
       ?>
       
    up in Apache with PHP 1. Save the file as upload.php in htdocs directory 2.Create a subdirectory called temp under htdocs 3. Change the url in the code to point to upload.php (you may need to use machine ip address instead of the localhost if you are debugging from eclipse) something like http://192.168.1.142:8080/upload.php 4. You should see the proper response once the file is uploaded. Try something around 10MB otherwise it may take a long time. 5. Uploaded file will be under htdocs/temp, open and verify if it is base64 encoded.
  9. Hieu Pham 2014-05-23

    3.3.X PR: https://github.com/appcelerator/titanium_mobile/pull/5733
  10. Samuel Dowse 2014-05-29

    Verified fixed on: Mac OSX 10.9.3 Appcelerator Studio, build: 3.3.0.201405271647 Titanium SDK, build: 3.3.0.v20140528144113 Titanium CLI, build: 3.3.0-beta Alloy: 1.4.0-beta Android Device: Nexus 4 (4.4) Large file successfully uploaded with base64 encoding. Closing.
  11. Ping Wang 2014-07-31

    update doc: https://github.com/appcelerator/titanium_mobile/pull/5942

JSON Source