Titanium JIRA Archive
Appcelerator Community (AC)

[AC-910] Unable to access ApplicationData file from WebView using an XMLHttpRequest on Android

GitHub Issuen/a
TypeBug
Priorityn/a
StatusResolved
ResolutionWon't Fix
Resolution Date2014-07-02T16:52:11.000+0000
Affected Version/sn/a
Fix Version/sn/a
Componentsn/a
LabelsXMLHttpRequest, webview
ReporterJerod Fritz
AssigneeRadamantis Torres-Lechuga
Created2014-06-29T16:07:09.000+0000
Updated2016-03-08T07:37:13.000+0000

Description

*Overview* On Android if I load a local file in a webview and request another local file from the Application Data Directory via XMLHttpRequest() the file fails to load. The xhr status returned remains 0. Is there any way to facilitate this type of request from within a webview to a local resource? *Reason for Need* We've developed an ePub document reader for iOS with a handful of features similar to iBooks or Kindle. By nature the ePub document needs to render in a WebView. When downloading and loading an ePub document from the web to display in the reader the WebView needs access to the Application Data directory. *Expected* Running this produces a console output of at lease one Pass test case on an Android device or Similator *Observed Results* All test paths fail on Android. On iOS this works *app.js*
var win = Ti.UI.createWindow();
win.add(Ti.UI.createWebView({
  url : "/index.html"
}));

var data = Ti.Filesystem.getFile(Ti.Filesystem.getApplicationDataDirectory() + "/hello.txt");
data.write("Hello World");

var paths = [];
paths.push(Ti.Filesystem.getApplicationDataDirectory() + "/hello.txt");
paths.push(Ti.Filesystem.applicationDataDirectory + "/hello.txt");
paths.push("/hello.txt");
paths.push("file:///hello.txt");
paths.push("appdata-private:///hello.txt");

Ti.App.addEventListener('webView:response', function(e) {
  if (e.success) {
    Ti.API.info(e.args.index + ": Pass [" + e.response + "] : " + e.args.path);
  } else {
    Ti.API.info(e.args.index + ":Fail : " + e.args.path);
  }
});

Ti.App.addEventListener('webView:runTests', function() {
  for (var i = 0; i < paths.length; i++) {
    Ti.API.info("trying " + paths[i]);
    Ti.App.fireEvent('webView:load', {
      path : paths[i],
      index : i
    });
  }
});

win.open();
*index.html*
<html>
  <head>
    <script>
      function load(args) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", args.path, true);
        xhr.onreadystatechange = function() {
          if (xhr.readyState === 4) {
            if (xhr.response) {
              Ti.App.fireEvent("webView:response", {
                success : true,
                args : args,
                response : xhr.response
              });
            } else {
              Ti.App.fireEvent("webView:response", {
                success: false,
                args : args,
                xhr : xhr
              });
            }
          }
        };
        xhr.send();
      }
      Ti.App.addEventListener('webView:load', load);
      Ti.App.fireEvent("webView:runTests");
    </script>
  </head>
</html>

Comments

  1. Mauro Parra-Miranda 2014-06-30

    Hello! You can access it, with a different logic. Check testcase:
       var win = Ti.UI.createWindow({
       	backgroundColor:'#fff'
       });
       
       var loadingLabel = Ti.UI.createLabel({
       	text:'Loading ...',
       	top:20,
       	left: 10
       });
       win.add(loadingLabel);
       
       
       var xhr = Titanium.Network.createHTTPClient({
       	onload: function() {
       		// first, grab a "handle" to the file where you'll store the downloaded data
       		var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,'mygraphic.png');
       		f.write(this.responseData); // write to the file
       		Ti.App.fireEvent('graphic_downloaded', {filepath:f.nativePath});
       	},
       	timeout: 10000
       });
       xhr.open('GET','http://cloud.appcelerator.net/cb0dbcc6-6fe3-4206-a32e-cf266c80aa3d/default_app_logo.png');
       xhr.send();
       
       Ti.App.addEventListener('graphic_downloaded', function(e) {
       
       	var file = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,'mygraphic.png');
       	
       	var webview = Ti.UI.createWebView({
       		url:file.resolve(),
       	});
       	win.remove(loadingLabel);
       	win.add(webview);
       });
       
       win.open();
       
    Let me know if that works for you. Best Regards,
  2. Jerod Fritz 2014-07-01

    Your example illustrates setting the url of the webview to the file in the applicationDataDirectory. This does not relate to the issue I am trying to illustrate above. The difference is that we are not trying to set the url of the WebView to a file in the applicationDataDirectory (your example). We are trying to read a file from local javascript in the WebView via the XMLHttpRequest to a file that resides in the applicationDataDirectory.
  3. Mauro Parra-Miranda 2014-07-01

    Hello [~jerodfritz]! Which one is the usercase for this? Maybe we can provide an alternative solution for the issue. Best Regards,
  4. Jerod Fritz 2014-07-01

    HI Mauro, Stated above in the "Reason For Need" we have implemented an ePub document reader in iOS that functions very well. In the process of converting to Android we have encountered an issue which I believe is due to a cross domain request issue that I'm hoping there is a workaround for. An ePub document is essentially a zip containing a series of html files. To build any type of reader you must render these html files within a webview. The webview must be capable of loading files stored in the Application's data directory where the epub was downloaded to. The example I provided simulates the types of requests the app must be able to make. - Jerod
  5. Mauro Parra-Miranda 2014-07-01

    Hello [~jerodfritz]! In that case, the recommendation would be take the sample code provided, get the zip within the mobile app and then you can open the webview just for display. Best Regards
  6. Jerod Fritz 2014-07-01

    Mauro. Thank you for looking into this, but you're still not understanding the problem. Can you take another look at the explanation.
  7. Adriano Paladini 2014-07-02

    Hi Jerod Fritz, I have a "e-book reader" published. Search on Stores for "Widbook". The way I did it was different, I carry a "model" (index.html), and inject the contents therein. Ie: I load the CSS and the text in a ready template. For this I have a template with html tags that I substitute with REPLACE with the contents of files and load in web view.
  8. Jerod Fritz 2014-07-02

    Thanks for the suggestion Adriano. We will explore that route.
  9. Radamantis Torres-Lechuga 2014-07-02

    The purpose of the XMLHttpRequest or HTTPRequest it's to transfer data between a client and a server, in this particular case, to try to access a local file using http request is something that is unsupported, I'll encourage you to use another approach to access the files that your reader needs. Like the one mentioned by [~mpmiranda]

JSON Source