cordova-plugin-wkwebview-engine icon indicating copy to clipboard operation
cordova-plugin-wkwebview-engine copied to clipboard

local files problem

Open pablomaurer opened this issue 8 years ago • 32 comments

While this plugin would be great, it's for many just not useable, because we can't access local files.

cdvfile://

  • url: cdvfile://localhost/library-nosync/images/img.jpg
  • error without csp: url not supported
  • error with csp: Refused to load cdvfile://localhost/library-nosync/images/img.jpg because it does not appear in the img-src directive of the Content Security Policy.

file:// (works in sim, but not on device)

  • url: file:///var/mobile/Containers/Data/Application/UUID/Library/NoCloud/images/img.jpg
  • error with and without csp: requested URL not found on this server

relative path (bundled inside www)

  • url img/placeholder.png
  • error: none but what if resources are downloaded/created after installation?

relative path (not bundled inside www)

Since the Filesystem is:

/var/mobile/Applications/<UUID>/
- appname.app/
-- www/
- Library
-- NoCloud

The relative path to NoCloud should be ../../Library/NoCloud/images/img.jpg.

  • url: ../../Library/NoCloud/images/img.jpg
  • error: same as file:// requested URL not found on this server

Related Issues

Issue #74 Issue #73 Issue #63 Issue #33 Issue #9 Issue #77 with PR #76 (fixes file:// under appname.app so won't help) Issue in cordova-plugin-file is marked as wontfix.

more

stackoverflow:

github:

webkit:

### even more It's also hard to google the problem, since there where a time where no file:// urls worked at all. After they fixed it, there remained the problem to access file:// via xhr/ajax. Now the problem is we can't access the documents and library directories and we are in appname.app, read more about the file system

But you can define a directory where relative url's are allowed apple docs loadfileurl

pablomaurer avatar Nov 21 '16 12:11 pablomaurer

Accessing local files is very important! Hoping for a fix soon.

dylanvdmerwe avatar Nov 29 '16 12:11 dylanvdmerwe

trying the hybrid way, did fail on all ways:

  • UIWebview: because of a memory leak, doing many requests for db sync will result in app crash.
  • WKWebview: because of not beeing able to access synced assets (images/pdfs) after installing the app.

pablomaurer avatar Dec 01 '16 10:12 pablomaurer

Hi again, since giving up is not an option 😄

Found a cordova-webserver, easy to configure and use:

https://github.com/floatinghotpot/cordova-httpd There is also a open PR to serve multiple folders open, which also is cool.

I do server the folder

/Users/pm/Library/Developer/CoreSimulator/Devices/<UDID>/data/Containers/Data/Application/<AppId>/

So I have access to all files. 🎉

The angular service I made: ```js function webserverService($ionicPlatform, $q) {
"use strict";
console.info("------>  webserver Service loaded");

// private variables
// ----------------------

// exposed
// ----------------------
var service = {
    start: startServer,
    stop: stopServer,
    getUrl: getUrl,
    getLocalPath: getLocalPath,
    isRunning: isRunning
};
return service;

// functions not in plugin
// ----------------------
function getPlugin() {
    var defer = $q.defer();

    $ionicPlatform.ready().then(function () {
        if (cordova && cordova.plugins && cordova.plugins.CorHttpd) {
            defer.resolve(cordova.plugins.CorHttpd);
        } else {
            console.error("webserverService: CorHttpd not found");
            defer.reject("CorHttpd not found")
        }
    });

    return defer.promise;
}

function isRunning() {
    var defer = $q.defer();

    getUrl().then(function (url) {
        if(url.length > 0) {
            defer.resolve(true)
        } else {
            defer.resolve(false)
        }
    });

    return defer.promise;
}

// functions
// ----------------------
function startServer(options) {
    var defer = $q.defer();

    console.log('serving from ', cordova.file.applicationStorageDirectory.replace( 'file://', '' ));

    isRunning().then(function (isRunning) {
        if (isRunning) {
            console.warn('webserverService: already running');
            defer.resolve(true);
        } else {
            return getPlugin();
        }
    }).then(function (httpd) {
        httpd.startServer({
            www_root : cordova.file.applicationStorageDirectory.replace( 'file://', '' ) || options.www_root,
            port : 8080 || options.port,
            localhost_only: true || options.localhost_only
        }, function(url) {
            console.log('webserverService: server started at ', url);
            getLocalPath().then(function (path) {
                console.log('webserverService: localPath', path);
            });
            defer.resolve(url)
        }, function (error) {
            console.error('webserverService: could not start webserver');
            defer.reject(error);
        })

    });

    return defer.promise;
}

function stopServer() {
    var defer = $q.defer();

    getPlugin().then(function (httpd) {
        httpd.stopServer(function() {
            defer.resolve(true);
        }, function(error) {
            defer.reject(error);
        });
    });

    return defer.promise;
}

function getUrl() {
    var defer = $q.defer();

    getPlugin().then(function (httpd) {
        httpd.getURL(function(url) {
            defer.resolve(url);
        }, function (error) {
            defer.reject(error);
        });
    });

    return defer.promise;
}

function getLocalPath() {
    var defer = $q.defer();

    getPlugin().then(function (httpd) {
        httpd.getLocalPath(function(path) {
            defer.resolve(path);
        }, function (error) {
            defer.reject(error);
        });
    });

    return defer.promise;
}

}

</details>

pablomaurer avatar Dec 06 '16 12:12 pablomaurer

This issue is on the todo list of the ionic developers to look into. Be patient.

dylanvdmerwe avatar Dec 06 '16 12:12 dylanvdmerwe

I'm shure Ionic is doing what they can and they have a lot to do. But the workaround with the local webserver is working. So no need to be anymore patient :)

pablomaurer avatar Dec 06 '16 12:12 pablomaurer

The workaround presented by @mnewmedia is very similar to the approach used by the Telerik WkWebview plugin. Unfortunately, that plugin is poorly maintained (most notably, it doesn't support Cordova iOS 4+).

It seems like running a web server is the only way to fully solve the file:/// protocol issues of WkWebview, which means that it is probably smarter to serve the entire app through the web-server rather than just assets. Doing so would remove the need for the XHR polyfill and probably simplify this project a lot.

It is really great that the Ionic crew is building a new wkwebview plugin, as this has been a weak part of the Cordova toolchain for a long time. I do hope that they'd consider the webserver approach, as it would probably be more robust and remove the issues surrounding the XHR polyfill (external library compatibility).

kristfal avatar Dec 08 '16 09:12 kristfal

I've been looking into this whole WKWebview + file:// issue for a long time now and came up with this PR: https://github.com/apache/cordova-plugin-wkwebview-engine/pull/26

I'm not entirely sure but I think it would replace the need for this fork entirely... would love to see feedback...

rubenstolk avatar Dec 25 '16 22:12 rubenstolk

@rubenstolk @mnewmedia @kristfal @dylanvdmerwe Can you reinstall WK and try again? WK 2.0 was released!! https://github.com/driftyco/cordova-plugin-wkwebview-engine/commit/114a9b190a06285d3ae6ac0384e85e0401942d56

Also, do not make requests to: file:///var/mobile/Containers/Data/Application/UUID/Library/NoCloud/images/img.jpg

use: /var/mobile/Containers/Data/Application/UUID/Library/NoCloud/images/img.jpg

instead

manucorporat avatar Jan 19 '17 19:01 manucorporat

@manucorporat I saw the code for 2.0 but I don't really understand how you've solved the file access. Can you explain the philosophy behind 2.0?

Really eager to try it out though! Especially wondering if it would also allow FileReader stuff...

rubenstolk avatar Jan 20 '17 07:01 rubenstolk

@rubenstolk of course. Basically we fool WKWebView into thinking it is performing a HTTP request, but actually there is not a HTTP server involved.

I found a way to hijack the request at the Foundation (apple framework) level, this way we override the current behavior of a HTTP request if the host === ionic.local.

Instead of performing the request, we read the path:

http://ionic.local/var/mobile/Containers/Data/Application/UUID/Library/NoCloud/images/img.jpg

we read the disk normally, then we fulfill a NSURLMutableResponse, and we return it back to WK

manucorporat avatar Jan 20 '17 13:01 manucorporat

@manucorporat but it wouldn't solve access to uploaded files right? I think I'll have to stay with https://github.com/apache/cordova-plugin-wkwebview-engine/pull/26 for another while...

rubenstolk avatar Jan 20 '17 13:01 rubenstolk

yes, I would solve the access to uploaded files!

manucorporat avatar Jan 20 '17 13:01 manucorporat

Oh hey, this indeed works... that's kind of magic!

rubenstolk avatar Jan 20 '17 15:01 rubenstolk

@rubenstolk told you!!! haha

Also this new plugin will allow us to run all the image prefetching in ion-img inside web workers, expect HUGE performance improvements in Virtual List with hundreds of images (Instagram-like apps)

manucorporat avatar Jan 20 '17 15:01 manucorporat

Do you have any documentation on how to use web workers?

rubenstolk avatar Jan 20 '17 15:01 rubenstolk

no yet, we are still figuring out a lot things :)

manucorporat avatar Jan 20 '17 15:01 manucorporat

@manucorporat I think there would be merit in releasing the version with the file access solved as a separate (non-ionic-specific) branch. It would help non ionic users as well.

rubenstolk avatar Jan 20 '17 15:01 rubenstolk

Can confirm that files such as images work, but others such as videos do not. This includes reading from assets and on the file system. @manucorporat I have sent you a test project to help reproduce this.

dylanvdmerwe avatar Jan 22 '17 17:01 dylanvdmerwe

@manucorporat this is looks really great! I have just installed from the git repo been reading through these comments to try and work out how to address local files. I was thinking it would be beneficial if urls starting with file:/// could be automatically replaced so that we don't have to perform any platform specific logic when using this plugin.

I'm more than happy to help you out with testing this in different scenarios if that is useful

ghenry22 avatar Jan 23 '17 04:01 ghenry22

This is great nice work, my only problem I'm using (file, file transfer, chromium zip) plugins and therefore using cdvfile:// and it wont navigate to the page. but at least it allows me to download the page in the first place. a definite step forward

putnal avatar Jan 24 '17 01:01 putnal

cdvfile urls should work fine passing references to files and file locations between plugins, I also use the file transfer, file and media plugins, all of which support cdvfile in their native code.

You can't use cdfile:// urls directly in your templates with wkwebview though. You can use the full path to the file:


//map the filesystem dataDirectory
this.fs = cordova.file.dataDirectory;

//path to access a file
this.targetPath = this.fs+this.imageCacheDir+"/"+"filename.jpeg"

//set the target download path for images (for ios slice off the file:/// for wkwebview)  
if(this.targetPath.startsWith("file:///") && this.isWKWebview){
      this.targetPath = this.targetPath.slice(7);
}

the resultant URL you CAN use directly in your javascript or in your template

ghenry22 avatar Jan 24 '17 12:01 ghenry22

Event I didn't try it out yet, but i will soon =) I would like to say: Thank You @manucorporat 🎉 ❤️ 😄

pablomaurer avatar Jan 24 '17 13:01 pablomaurer

@manucorporat just found out that while using regular XHR's to external hosts, no JSON body is being sent.

I'm getting more convined that we should put our energy in convincing Webkit/Apple to open up the api for: "https://github.com/apache/cordova-plugin-wkwebview-engine/pull/26" so that we can stay away from hacky workarounds...

rubenstolk avatar Jan 26 '17 09:01 rubenstolk

@manucorporat As you suggested, I used the latest code to try to show an image:

<img src="/var/mobile/Containers/Data/Application/UUID/Library/NoCloud/images/img.png" />

It threw the following errors on iOS10.2-Device. This worked fine on the iPhoneSimulator(10.2). It worked fine for both if I use UIWebview.

[Error] Failed to load resource: The operation couldn’t be completed. (kCFErrorDomainCFNetwork error 1.)
[Error] Failed to load resource: The operation couldn’t be completed. Operation not permitted

wasedaigo avatar Mar 25 '17 08:03 wasedaigo

@manucorporat thx for the plugin version 2!

the really neat tricks you described above to do not index images with file:// but without shouldn't be documented in the README? I guess many persons could be interested by that nice workaround

peterpeterparker avatar Mar 26 '17 12:03 peterpeterparker

plz revert back to ionic.local instead of localhost as it's easier to whitelist them

awebdeveloper avatar Mar 26 '17 14:03 awebdeveloper

@manucorporat thanks! Stripping file:// from the URL worked for us.

fourcube avatar Apr 27 '17 15:04 fourcube

@ghenry22 What you've done looks really elegant! How do you do feature detection like this? this.isWKWebview

krishnagopinath avatar Apr 27 '17 21:04 krishnagopinath

@krishnagopinath it is a small function that checks if the platform is iOS and if indexdb is available as in the readme for this repo. Seems to work. Make sure to check for iOS as well though, otherwise android systems with indexDB will misdetect.

ghenry22 avatar May 17 '17 10:05 ghenry22

Hi guys, Is this working in recent versions? Thank you!

pcsantana avatar Nov 22 '17 15:11 pcsantana