apache / cordova-plugin-file

Apache Cordova File Plugin
https://cordova.apache.org/
Apache License 2.0
741 stars 756 forks source link

File Path Issue in iOS 13.1 #346

Open GriffinLedingham opened 4 years ago

GriffinLedingham commented 4 years ago

Bug Report

Problem: Local file path returns incorrectly on iOS 13.1

What is expected to happen?

cordova.file.applicationDirectory returns a file path which is supported by resolveLocalFileSystemURL to parse out a local path path for use in a cordova app.

What does actually happen?

cordova.file.applicationDirectory now returns a file path prefixed with /private/ which is incorrectly parsed by resolveLocalFileSystemURL to produce an incorrect file path for use in a cordova app.

Information

This issue exists is the iOS 13.1 beta, which is due to release at the end of September. Being this early in a beta, it's difficult to determine whether iOS is broken, or the plugin is broken, but to be proactive I am assuming the latter.

I've dug through the 13.1 release notes, and don't see anything jumping out at me that'd modify the local file storage directory paths. With recent changes to the HTML5 app guidelines over the Summer, I figure this could be a rogue change of sorts to modify how apps are being sandboxed/given access to the file system.

Testing to reproduce this, I was building my iOS app to two physical iPhone XR's, one on iOS 13 Gold Master, and one on iOS 13.1 Beta 3 and comparing file paths returned by the plugin.

Command or Code

cordova.file.applicationDirectory window.resolveLocalFileSystemURL

Environment, Platform, Device

iPhone XR iOS 13.1 Beta 3

Version information

cordova-plugin-file 6.0.2

Checklist

boredom2 commented 4 years ago

Hi there,

this Problem also occurs on iOS 13 "Live Version", so its not Beta-related. On iOS 13, selecting a File (Video, in our Case), leads to a Path similar to:

"file:///private/var/mobile/Containers/Data/PluginKitPlugin/D61F8E95-612D-4CB3-94BB-26ADDF68F26A/tmp/trim.76E14050-7A9F-430F-B13F-1A8758033345.MOV"

which breaks window.resolveLovalFileURL with FileError 1.

This happens on any physical Devices, we tested it so far - but for some Reasons, does not happen in an emulated Device in XCode.

A Fix for iOS13 or a Hint, how to work around this, would be really helfull.

Thanks, Christoph

GriffinLedingham commented 4 years ago

I’m wondering if this could be related to the sandbox security issue fixed in 13.1.1. Will investigate tomorrow and report back if 13.1.1 seems to fix this.

https://support.apple.com/en-ca/HT210624

boredom2 commented 4 years ago

13.1.1 is not fixing this - sadly...

GriffinLedingham commented 4 years ago

Yeah, tested this morning on my case as well and 13.1.1 did not help the matter.

RidClick commented 4 years ago

I have the same problem, has anyone managed to make it work on iOS 13?

rizz360 commented 4 years ago

iOS & iPad OS 13.1.2 were released a few hours ago.

Will test and report back...

Edit: I no longer receive paths starting with /private/ and my app is working again. So this seems to be fixed in 13.1.2!

boredom2 commented 4 years ago

Sadly, the Issue is not fixed in 13.1.2 - at least not for Videos, which we have Focus on. Selecting a Video from the Picker still leads to something like "file:///private/var/mobile/Containers/Data/PluginKitPlugin/097EF2B0-8AAE-4137-A1E8-ECE3EDFD3B01/tmp/trim.2D02472E-C28C-4DB6-8EBF-C220337544FC.MOV" which leads to Error Code 1. So from our Point of view, the Issue is still there :(

RidClick commented 4 years ago

iOS & iPad OS 13.1.2 were released a few hours ago.

Will test and report back...

Edit: I no longer receive paths starting with /private/ and my app is working again. So this seems to be fixed in 13.1.2!

Do you copy the temporary image to the cordova.file.applicationDirectory directory? Because I am copying the image to the directory and then showing it, in ios 10.X or 12.X it works but in 13.1.2 it still does not work.

rizz360 commented 4 years ago

iOS & iPad OS 13.1.2 were released a few hours ago.

Will test and report back...

Edit: I no longer receive paths starting with /private/ and my app is working again. So this seems to be fixed in 13.1.2!

Sorry I have to correct myself. A bug in my debug build acted as a workaround...it's still broken. private/ is no longer included in DirectoryEntry.fullPath - but fullPath is still way too long.

Instead of having something like www/assets/data/... in full path, it now contains var/containers/Bundle/Application/EF.........FE02/APPNAME.app/www/assets/data/... which is nearly the full nativeURL - only missing file:///.

GriffinLedingham commented 4 years ago

Still seeing the issue on our end with 13.1.2 unfortunately.

kkabell commented 4 years ago

This is probably not a lasting solution, but I managed to restore functionality for uploading videos from the photo library in my app, so I guess that's good :)

The problem is in cordova-plugin-camera: It seems that the path is actually correct, but that the url simply gets invalidated too soon, causing the file manager to not be able to find the file. The workaround is to keep a reference to the info object passed to the imagePickerController:didFinishPickingMediaWithInfo: method of the UIImagePickerControllerDelegate, and thus preventing the url from being invalidated.

This is what worked for me: https://github.com/kkabell/cordova-plugin-camera/commit/b1b0c9499b6b5c18d109671967e1218e05428e0a

RidClick commented 4 years ago

This is probably not a lasting solution, but I managed to restore functionality for uploading videos from the photo library in my app, so I guess that's good :)

The problem is in cordova-plugin-camera: It seems that the path is actually correct, but that the url simply gets invalidated too soon, causing the file manager to not be able to find the file. The workaround is to keep a reference to the info object passed to the imagePickerController:didFinishPickingMediaWithInfo: method of the UIImagePickerControllerDelegate, and thus preventing the url from being invalidated.

This is what worked for me: kkabell/cordova-plugin-camera@b1b0c94

I have added the lines of your code to the camera plugin, removed and re-added the iOS platform and compiled, it is true that the path of the photo seems correct, but it still does not show the photo that I copied and pasted in the cordova.file.applicationDirectory in iOS 13.1.2

DanielLeberle commented 4 years ago

Please have a look at this issue in cordova-plugin-camera: https://github.com/apache/cordova-plugin-camera/issues/506

The fix provided in this comment worked for us!

JianhuisHuang commented 4 years ago

Sadly, the Issue is not fixed in 13.1.2 - at least not for Videos, which we have Focus on. Selecting a Video from the Picker still leads to something like "file:///private/var/mobile/Containers/Data/PluginKitPlugin/097EF2B0-8AAE-4137-A1E8-ECE3EDFD3B01/tmp/trim.2D02472E-C28C-4DB6-8EBF-C220337544FC.MOV" which leads to Error Code 1. So from our Point of view, the Issue is still there :(

I have the same issue, are you fixed or found solution?Thanks. @boredom2

adamjpeterson commented 4 years ago

Am am having this save issue in 13.1.2. Would be grateful to anyone who finds a solution to accessing resolveLocalFileSystemURL when selecting a video from the gallery.

tmk1991 commented 4 years ago

@DanielFreiburger

I applied that fix and it didn't work. I keep getting an ENCODING_ERR when calling readAsArrayBuffer

As mentioned below. the file oddly still has this weird prefix trim.0932D2E4-2811-49EF-8D12-6FA86EFDCA34.MOV https://github.com/apache/cordova-plugin-camera/issues/506#issuecomment-544104741

@kkabel 's fix is confirmed working!

benmcmaster commented 4 years ago

@kkabell it seems like it's working and resolveLocalFileSystemURL returns a url. However, when I try to load that url using the <video> tag I get and error:

[Error] Not allowed to load local resource: file:////var/mobile/Containers/Data/PluginKitPlugin/xxxxxxx/tmp/trim.zzzzzz.MOV

Any ideas?

breautek commented 4 years ago

@kkabell it seems like it's working and resolveLocalFileSystemURL returns a url. However, when I try to load that url using the <video> tag I get and error:

[Error] Not allowed to load local resource: file:////var/mobile/Containers/Data/PluginKitPlugin/xxxxxxx/tmp/trim.zzzzzz.MOV

Any ideas?

Sounds like you're CSP is not setup properly. I've provided some references below.

https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-whitelist/#content-security-policy https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/media-src

You need to allow the file: protocol, by having file: in the appropriate directive.

benmcmaster commented 4 years ago

@breautek Thanks! my CSP was fine... I had also upgraded my webview to cordova-plugin-ionic-webview which required me to use window.Ionic.WebView.convertFileSrc() to get the proper url for local files.

sudarshanadayananda commented 4 years ago

This is probably not a lasting solution, but I managed to restore functionality for uploading videos from the photo library in my app, so I guess that's good :)

The problem is in cordova-plugin-camera: It seems that the path is actually correct, but that the url simply gets invalidated too soon, causing the file manager to not be able to find the file. The workaround is to keep a reference to the info object passed to the imagePickerController:didFinishPickingMediaWithInfo: method of the UIImagePickerControllerDelegate, and thus preventing the url from being invalidated.

This is what worked for me: kkabell/cordova-plugin-camera@b1b0c94

you save my day dude. thanks a lot.

08Thug commented 4 years ago

Well the problem is with the apache cordova camera plugin it provides the temporary path to the video file to come out from this error implement below changes in camera plugin.

in CDVCamera.m change THIS:

(CDVPluginResult*)resultForVideo:(NSDictionary*)info
{
NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString];
return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath];
}

to THIS:

(CDVPluginResult*)resultForVideo:(NSDictionary*)info
{
NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path];

NSArray* spliteArray = [moviePath componentsSeparatedByString: @"/"];
NSString* lastString = [spliteArray lastObject];
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:lastString];
[fileManager copyItemAtPath:moviePath toPath:filePath error:&error];

return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath];
}

Hope it will help you.

And ofcourse you should perform this also

 if (this.platform.is('android')) {
    filePath = 'file:///' + this.uploadForm.controls['file'].value;
  } else {
    filePath = this.uploadForm.controls['file'].value;
  }
wingo7 commented 4 years ago

This is probably not a lasting solution, but I managed to restore functionality for uploading videos from the photo library in my app, so I guess that's good :)

The problem is in cordova-plugin-camera: It seems that the path is actually correct, but that the url simply gets invalidated too soon, causing the file manager to not be able to find the file. The workaround is to keep a reference to the info object passed to the imagePickerController:didFinishPickingMediaWithInfo: method of the UIImagePickerControllerDelegate, and thus preventing the url from being invalidated.

This is what worked for me: kkabell/cordova-plugin-camera@b1b0c94

Unfortunately, doesn't work for me ;(

crapthings commented 4 years ago

iphone6s 12.0 same here

works on simulator, but not real device

nvahalik commented 3 years ago

I recently updated my copy of cordova-plugin-camera to use a branch that includes https://github.com/apache/cordova-plugin-camera/commit/bfa38fed2c9141d614ebbbe5786f3904f7a90202 and this issue has been resolved for me.

inu-web commented 3 years ago

Maybe this may help anybody who struggles with this too ... we had the exact same effect window.resolveLocalFileSystemURL(cordova.file.applicationDirectory ) always faild with FileError 5 (ENCODING_ERROR) on real device (iPadAir 4th Gen) -> Always File Error 5 But it worked on The iPadAir Simulator(4.th Gen) without problems ...

after hours and hours bangign our heads against all walls in the office We were able to resolve this by: "not using es6" in the callback for success --- the good old way works on Simulator & Device CONST filePath = cordova.file.applicationDirectory + "www/" window.resolveLocalFileSystemURL(filePath ,function(fileEntry){ ... }); hope this helps

BanTheRewind commented 3 years ago

I resolved this by using the application storage on iOS, rather than the "data directory".

function getDataDirectory() {
    if (device.platform == "iOS") {
        return `${cordova.file.applicationStorageDirectory}Documents`;
    }
    return `${cordova.file.dataDirectory}`;
}

...

window.resolveLocalFileSystemURL(getDataDirectory(), (directoryEntry) => {
  // Enjoy cross-platform success here
}
itrafimovich commented 3 years ago

I've solved the same problem in other environment

It’s look like iOS bug (detected only in Swift code) path=file:///private/var/mobile/Containers/Shared/AppGroup/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXX/folder/

let result = fileManager.fileExists(atPath: path, isDirectory: &isDirectory) // false let result = fileManager.fileExists(atPath: path.absoluteURL.path, isDirectory: &isDirectory) // true

Using path.absoluteURL.path without prefix 'file://' path.absoluteURL.path=/private/var/mobile/Containers/Shared/AppGroup/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXX/folder/

Hope it will help

codal-shubhamb commented 2 years ago

Maybe it is not the issue of file plugin, It seems like it is issue of Cordova-camera plugin.

I have solved this issue just by removing and adding camera plugin in my application, You should try this too. The changes in Cordova-camera plugin which solves the issue are as following: Filename - https://github.com/apache/cordova-plugin-camera/blob/master/src/ios/CDVCamera.m and just search "ResultForVideo" and there are these three lines are added in latest plugin // On iOS 13 the movie path becomes inaccessible, create and return a copy
if (IsAtLeastiOSVersion(@"13.0")) {
moviePath = [self createTmpVideo:[[info objectForKey:UIImagePickerControllerMediaURL] path]];
}

huyewen commented 2 years ago

This is probably not a lasting solution, but I managed to restore functionality for uploading videos from the photo library in my app, so I guess that's good :)

The problem is in cordova-plugin-camera: It seems that the path is actually correct, but that the url simply gets invalidated too soon, causing the file manager to not be able to find the file. The workaround is to keep a reference to the info object passed to the imagePickerController:didFinishPickingMediaWithInfo: method of the UIImagePickerControllerDelegate, and thus preventing the url from being invalidated.

This is what worked for me: kkabell/cordova-plugin-camera@b1b0c94

Wow, so cool, my issue finally is solved because of your reply what you give here, thank you very much.

aehlke commented 10 months ago

maybe adjacently useful code, borrowed from thebaselab/codeapp which uses this logic to cover the /private/ issue in another context

fileprivate func getRootDirectory() -> URL {
    // We want ./private prefix because all other files have it
    if let documentsPathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        .first
    {
        #if targetEnvironment(simulator)
            return documentsPathURL
        #else
            if let standardURL = URL(
                string: documentsPathURL.absoluteString.replacingOccurrences(
                    of: "file:///", with: "file:///private/"))
            {
                return standardURL
            } else {
                return documentsPathURL
            }
        #endif
    } else {
        fatalError("Could not locate Document Directory")
    }
}
MustakTalukder commented 2 months ago

Hi All, Go to your Xcode, And follow this comment, just replace the code. It works for me.