cordova-plugin-file
cordova-plugin-file copied to clipboard
Accessing cdvfile:// from http://[network-url] in ajax throws CORS policy error on Android
While developing I run my app on an Android device. The app is served from a network url using cordova-plugin-webpack LiveReload (HMR) feature.
I've successfully implemented a download mechanism of JSON files and Audio files.
I can embed the downloaded audio file using cdvfile
protocol (obtained through .toInternalURL
) but I can not get the JSON files using Ajax requests because of this error:
Access to XMLHttpRequest at 'cdvfile://localhost/files/test/data.json' from origin 'http://10.123.123.123:8080' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
Recap:
- [x] I can access APIs on the web via ajax.
- [x] I can download files using XHR and store them on the device.
- [x] I can embed the downloaded files through src='cdvfile://'
- [ ] I can't ajax 'cdvfile://'
I have <access origin="*" />
and also tried <access origin="cdvfile://*" />
in config.xml
I have <allow-navigation href="cdvfile:*" />
CSP is set as follow:
default-src * cdvfile: data: blob: ws: wss: gap://ready file: http: https: 'unsafe-eval'; media-src * cdvfile:; style-src * 'unsafe-inline'; script-src * cdvfile: 'unsafe-inline' 'unsafe-eval'; connect-src * ws: wss:;"
Any help would be appreciated
Have you tried sending the cdvfile path into a toNativeURL() call to get back a valid url you can then use in the ajax call? Think it'll convert from a cdvfile path to a file:/// path
I've tried toUrl and toNativeURL (deprecated) but i get the same error as long as the allowed schemes are "http, data, chrome, https". Is there any way to change those?
sorry that didnt work, not sure
Why don't you use the actual file reader APIs instead of XHR? XHR is intended to make requests to remote servers, not to "download" local files.
https://github.com/apache/cordova-plugin-file#read-a-file-
Not 100% confident, but I don't believe there is a way in the android sdk to disable cors in the webview.
I do download the file from remote API (this part works) and I store it for offline usage. But then I have to load it.
I've tried using a file reader, but reading files is very slow (the files are huge).
The workaround (ugly) I'm thinking about is to edit the JSON, prepend a function call and load it through jsonp.
Being able to disable CORS for cdvfile
or file
protocol on the webview would be a lot nicer and I can reuse code (I'm porting an Electron app).
I briefly did some research and it looks like disabling CORS from the webview is not possible... but looks like it is possible to get around that by intercepting requests as shown here
And in case the link goes away, I'll post the example:
public class OptionsAllowResponse {
static final SimpleDateFormat formatter = new SimpleDateFormat("E, dd MMM yyyy kk:mm:ss", Locale.US);
@TargetApi(21)
static WebResourceResponse build() {
Date date = new Date();
final String dateString = formatter.format(date);
Map<String, String> headers = new HashMap<String, String>() {{
put("Connection", "close");
put("Content-Type", "text/plain");
put("Date", dateString + " GMT");
put("Access-Control-Allow-Origin", /* your domain here */);
put("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS");
put("Access-Control-Max-Age", "600");
put("Access-Control-Allow-Credentials", "true");
put("Access-Control-Allow-Headers", "accept, authorization, Content-Type");
put("Via", "1.1 vegur");
}};
return new WebResourceResponse("text/plain", "UTF-8", 200, "OK", headers, null);
}
}
// WebViewClient
@Override
@TargetApi(21)
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (request.getMethod().equalsIgnoreCase("OPTIONS")) {
return OptionsAllowResponse.build();
}
return null;
}
Not sure if this will work at all, and I can't promise when I'll have time to experiment with this.
How large of a file are we talking about? In one of my apps, I read/write JSON files of approximately 50mb, but sometimes upwards of 100mb with no significant slowdown. I do use the deprecated file transfer plugin to avoid reading the data in the javascript environment though. But the data is recorded and JSON stringified in javascript for the initial write.
My files are smaller, 10-15MB but the reading takes up to 4 seconds on my (old) test device using just the file plugin.
If I don’t find any alternative I’ll conform with that.
PS: thanks for investigating further!
Finally I opted for the script embedding mechanism:
- while writing the json during the download operation, I wrap it in a function call
window.__loadJSON(filePath, [json-content]);
- When I need it I register a callback bound to
filePath
and I load it using a script tag -
__loadJSON
triggers the callback
it's still slow (I assume it's my device) and ugly. But it's a little faster then the read implementation and probably more solid.
It's silly I can load and run a fully functional script but I can't load a simple json object from the same source / protocol.
Glad you found a workaround.
I'm going to add the help wanted label to this ticket. I think it's still worth investigating on request intercepts to see if it can be used to allow cdvfile
and other custom protocols through CORS.
It appaers the "cdvfile://" protocol causes a lot of trouble.
Related issues:
Issue #295 > PR #296 + PR #322
Issue #329
Issue #347
Issue #349
It seems like a solution similar to the one proposed in pull request #296, might also work for this situation.
@Lindsay-Needs-Sleep I'm facing a lot of troubles. I've seen you've done some PR, do you managed to load local files (using XHR and/or regular request) in both iOS and Android even if current page is on a remote host?
@vitto32 I don't think I have any particularily good solutions for you. Defeating CORS as suggested by @breautek seems like it would be the nicest solution.
Here are a couple ways that could work:
-
You can use cordova-plugin-ionic-webview
This runs/(fakes?) a local server on the device which hosts your files. I had to use this to play locally saved videos.
++ You should definitely be able to xhr/ajax request whatever you want -- It maybe runs a local server (depends/not sure). If you have to support ios less than 11, you have to use the 2.x version which definitely runs an actual server (on ios at least). Otherwise, 4.x on ios, does not actually run a server. I'm not entirely sure how the android implementation does it. -
Have your app inject code (exec I believe) directly into the webview, onto your remotely hosted web page using cordova-plugin-hostedwebapp. It is not maintained but it works, (basically), you just need to select a more a active fork to use. ++ definitely no local server ++ nice way to load all the cordova files + plugin files onto your remote webpage without having to rely on cdvfile -- you would have to rewrite your json as a js script and load it as a script I believe (so that it can be injected) -- You have to know which scripts should be injected on which pages at build time
@Lindsay-Needs-Sleep thanks! My project evolved, and now I need real XHR support to load not only json files but also binary content and possibly chunks of data (mainly encoded audio files).
Android I solved it using a slightly different version of PR #322 (i used the url format proposed in #296). It seems to work smoothly even if my www folder is on a remote host. I haven't tested it yet in prod mode (www folder under file://) but I'm pretty confident it will work.
iOS I'm using cordova-plugin-wkwebview-engine
but at the moment I have no access to xCode so I just have a few possible roads in mind:
- try
cordova-plugin-wkwebview-file-xhr
that sounds pretty interesting - try
WKURLSchemeHandler
/setURLSchemeHandler
approach as you suggested in your PR (targeting iOS >= 11)
everything started from your precious links to PR and Issues dealing with this. Thanks!
The cordova-plugin-ionic-webview
also sounds as a feasible plan B.
Hi, Try this solution:
Search the code for the following line: (might be in several files, so do it for all of them)
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
And add the following two lines after it:
[configuration.preferences setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"];
[configuration setValue:@"TRUE" forKey:@"allowUniversalAccessFromFileURLs"];
Since Cordova 10 Android template does all requests via https how is cvdfile:// access via browser supposed to work now. We needed to upgrade and have found ourselves stuck on this issue. All imgs from webview that were cached can no longer be rendered. I've tried so many hacks at this but setting a host in config.xml and using android intended behaviour just means we are stuck at accessing the cvdfile protocol. Is this right, or are we doing something wrong. ionic cordova webview is not compattible with our other plugins for encryption and I don't understand how IOS allows access while android doesnt. Is this plugin just broken in Cordova Andoid 10?