Error when downloading file from Flutter Amplify Storage - ios

I get the error below when downloading an audio file from Amplify Storage on an iOS device, both Simulator and a device. It all works perfectly fine when doing it on an Android emulator and device.
When downloading a JSON-file from the Storage it's working on both platforms without errors.
The JSON I'm downloading in the app as a guest, while the audio file is downloaded as private, but I don't see why this should make a difference when it's working on Android.
StorageException(message: Unexpected error occurred with message: Domain: [NSCocoaErrorDomain
Code: [4
LocalizedDescription: [The file doesn’t exist.
LocalizedFailureReason: [The file doesn’t exist.
LocalizedRecoverySuggestion: [, recoverySuggestion: This should not happen. There is a possibility that there is a bug if this error persists. Please take a look at https://github.com/aws-amplify/amplify-ios/issues to see if there are any existing issues that match your scenario, and file an issue with the details of the bug if there isn't.
Issue encountered at: file: /Users/x/Documents/Github/TransscriberAI-App/Flutter/transscriber/ios/Pods/Amplify/Amplify/Categories/Storage/Error/StorageError.swift
function: recoverySuggestion line: 63, underlyingException: null)
Here is the code I use to download the file:
Future<String> getAudioPath(String key) async {
final docDir = await getTemporaryDirectory();
final filePath = docDir.path + '/audio/$key';
File file = File(filePath);
final S3DownloadFileOptions options = S3DownloadFileOptions(
accessLevel: StorageAccessLevel.private,
);
if (await file.exists()) {
return filePath;
} else {
try {
await Amplify.Storage.downloadFile(
key: key,
local: file,
options: options,
);
return filePath;
} on StorageException catch (e) {
print('Error downloading file: $e');
return 'null';
}
}
}
Update:
I have now created an issue on the Amplify flutter Github
https://github.com/aws-amplify/amplify-flutter/issues/1276

The problem was caused by the /audio/ folder not existing on iOS. This answer tells how you can create the path, if it doesn't exist.

Related

React Native HTML to PDF not displaying local images

In case you guys don't know, there was a problem previously with this library not rendering local images on Android as well, but apparently it was solved. Now, I'm facing the exact same issue on iOS, with a difference that I can use static images like assets/src/assets/images/logo.png. But when the images start with something like file:///, storage://, ph:// it simply does not get rendered.
What I'm doing is trying to generate a PDF report file, which must be generated independently the user has an internet connection or not. That is the reason why I have to use local images.
The static image is the logo of the company, and the local image which is not getting rendered is an image saved to the phone's storage through Image Picker or Camera Roll. The React Native Image component displays the image perfectly, so I don't think I'm using a wrong path.
What I have tried so far:
Removing the file:/// or storage:// or ph:// from the beginning of the path string;
In some cases, when I save an image to the phone's library with Camera Roll, it will return a path that starts with ph:// but without an extension, such as .jpg or .png. I tried to put the extension manually, and still does not make any difference;
I tried to convert the image to base64 using rn-fetch-blob (with RNFetchBlob.fs.base64.encode(path)), but still got no success.
Devices:
iPhone SE with iOS 14 (also simulator iPhone 11 with iOS 15)
MacBook Air 2017 Core i5 1.8GHz and 8gb RAM (macOS Big Sur 11.5.2)
Environment
node: 12.22.7
npm: 6.14.15
react: 16.9.0
react-native: 0.61.5
react-native-html-to-pdf: ^0.11.0 (updating it to 0.12.0 also got me the same result)
Code:
sharePDF = async () => {
try {
this.changeVisibilityOptions(false);
this.changeVisibilityLoading('Gerando PDF...');
let htmlTemplate = '';
htmlTemplate = await getPDFDespesa(this.state);
const pdfOptions = {
html: htmlTemplate,
fileName: 'RelatorioDespesas',
directory: 'Relatorios'
};
let pdfFile = await RNHTMLtoPDF.convert(pdfOptions);
this.changeVisibilityLoading(false);
const shareOptions = {
title: 'Compartilhar com:',
url: `file://${pdfFile.filePath}`,
type: 'application/pdf',
failOnCancel: false
};
const ShareResponse = await Share.open(shareOptions);
} catch (error) {
this.setState({ visibilityLoadingScreen: false });
console.log('Error =>', error);
}
}
Final thoughts:
Well, since the code is stored at a private repository, I can't show the whole thing here for ethical reasons. But I'm doing my best to give you as much details as possible.
The output the code produces an almost complete PDF, with the only point that I see broken image icons where the images were supposed to be. For Android it works perfectly now.
I think this might be an issue related to WebView, since react-native-html-to-pdf uses WebView to generate the PDF from HTML code. I reached this conclusion after another developer at my job was trying to create a screen with a preview of the PDF before it could be shared got the very same problem for both Android and iOS. The library he used was react-native-webview.
Update with solution
Alright guys, after a long time of research, me and a colleague got to a solution which may not be the best but does what we expected.
First of all, one thing that was discovered is that we have to divide the problem in two, because we actually had two problems.
Images from react-native-image-picker: After a long time trying to find the problem which was preventing the local images from getting rendered, I tried updating the library to version 4.7.3 (latest version at that day) and did a number of required changes to the code, as the version we were using was considerably aged. Well, it happened to work out for my surprise, even with the response uri's format not being changed;
Images from #react-native-community/cameraroll: This one was a bit more complicated. It took me some time to realize that the iOS' PHAsset was not supported in the WebView or react-native-html-to-pdf (which uses WebView in background). So, after some research, me and my colleague found a workaround that lead us to a relatively easy solution. Basically we used react-native-fs to copy the PHAsset media file to a temporary directory, which would return a uri that started with file:// and could be rendered by WebView. That's the code we used to do this:
export default function getImageNameFromUrl(imageUrl = "") {
if (imageUrl) {
const splittedImageUrl = imageUrl.split('/');
return splittedImageUrl.pop();
}
return null;
};
export default async function copyAssetsFileIOSAndReturnURI(remoteURL = '', localURI = '') {
try {
if (remoteURL && localURI) {
const imageName = getImageNameFromUrl(remoteURL);
const imgPath = await RNFS.copyAssetsFileIOS(localURI, RNFS.TemporaryDirectoryPath+imageName, 0, 0);
return imgPath;
}
return null;
} catch (err) {
console.log(err);
return null;
}
}

Unity iOS build in Xcode cannot write downloaded images from Firebase Storage

In the Unity Editor, the app works well with the Firebase storage. Images are downloaded and used. When we build the Unity app for iOS, it gave errors when it tries to download the images from the Firebase Storage. I think, this is an issue to writing permission, and I have no clue on how to solve it from Unity code
What was done
Clean Build Folder
Update to Latest Firebase packages
Refer to these SO questions:
Firebase Storage download to local file error
Images not downloading from Firebase Storage
Error when downloading from Firebase storage
These threads aren't using Unity, and I don't know how these can be applied to the issue at hand
Code
string savePath = Application.persistentDataPath + "/" + data.banner;
StorageReference storageRef = storage.GetReference(data.banner);
Task task = storageRef.GetFileAsync(savePath, new StorageProgress<DownloadState>((DownloadState state) => {
GetComponent<DatabaseManager>().UpdateProgress(state.BytesTransferred, state);
}), CancellationToken.None);
task.ContinueWithOnMainThread(resultTask => {
if (!resultTask.IsFaulted && !resultTask.IsCanceled && resultTask.IsCompleted) {
floorCounter++;
if (floorCounter == floors.Count) {
isFloorComplete = true;
}
} else {
Debug.Log(resultTask.Exception.ToString());
errorCaught = true;
return;
}
});
Errors
System.AggregateException: One or more errors occurred. ---> Firebase.Storage.StorageException: An unknown error occurred
--- End of inner exception stack trace ---
---> (Inner Exception #0) Firebase.Storage.StorageException: An unknown error occurred<---
<>c__DisplayClass3_1:<DownloadImages>b__3(Task)
System.Action`1:Invoke(T)
Firebase.Extensions.<ContinueWithOnMainThread>c__AnonStorey1:<>m__0()
System.Func`1:Invoke()
Firebase.<RunAsync>c__AnonStorey1`1:<>m__0()
System.Action:Invoke()
Firebase.ExceptionAggregator:Wrap(Action)
Firebase.Dispatcher:PollJobs()
Firebase.Platform.FirebaseHandler:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
The errors is not helpful and to make it worse, I don't know how to find the debug log mentioned at Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35). I've opened the Xcode project, and see no Runtime folder (even after showing hidden items)
Any idea how to solve this issue?
The solution was certainly not obvious.
Apparently, all I need to do was to prepend file:/// to the savePath variable, like so:
string savePath = "file:///" + Application.persistentDataPath + "/" + data.banner

Ionic - Ios - Cordova File Plugin - Error

I am developing a hybrid app targeting iOS & Android. I had to implement a photo-uploader so I implemented it through ImagePicker & File Cordova Plugins. I tested it on Android and everything was okay.
The problem is when I test it for iOS I get the image URIs through ImagePicker but File throws the following error:
[error] - ERROR Error: Uncaught (in promise):
FileError: {"code":5,"message":"ENCODING_ERR"}
I have seen plenty of issues related to this error but all of them are related to Android while I have a problem with iOS. I understand that is a really complex issue so if anyone could give me any piece of advice, I would be grateful.
More details about the issue:
this.imagePicker
.getPictures({
maximumImagesCount: 1,
})
.then(async (result : string[]) => {
let file = 'file://' + result[0];
let path = file.substring(0, file.lastIndexOf("/") + 1);
this.image = await this.file.readAsDataURL(path, file): // Line which throws the exception
});
PS: I've tried adding 'file://' in all the ways I could imagine =)
Thanks in advance
Thanks both of you who helped, I fixed my problem through changing the outputType to 1 (base64 dataURL) instead of 0 (URI).
This is the code that fixed my bug.
this.imagePicker
.getPictures({
outputType: 1,
})
.catch((e) => alert(e))
.then(async (results: string[]) => {
this.image = "data:image/jpg;base64," + results[0]
}
});
A posible explanation for this is that ImagePicker is copying from the ios gallery directory to a ImagePicker's temporary directory, then File is trying to read those copied files but they must have been deleted by iOS File System.

NSFileManager moveItem throws Error Code=513

I'm trying to move a file from a URL to another, however I get an error with code 513. I understand that this is a NSFileWriteNoPermissionError. I don't see how this possible considering I created the folder in the first place.
//self.video!.folderURL.path = /var/mobile/Containers/Data/Application/066BDB03-FD47-48D8-B6F8-932AFB174DF7/Documents/AV/769c504203024bae95b47d78d8fe9029
try? fileManager.createDirectory(atPath: self.video!.folderURL.path, withIntermediateDirectories: true, attributes: nil)
// Update permission for AV folder
do {
let parentDirectoryPath = self.video!.folderURL.deletingLastPathComponent().path
let result = try fileManager.setAttributes([FileAttributeKey.posixPermissions: 0o777], ofItemAtPath: parentDirectoryPath)
print(result)
} catch {
print("Error = \(error)")
}
// sourceURL = file:///private/var/mobile/Containers/Data/PluginKitPlugin/50D8B8DB-19D3-4DD6-93DD-55F37CF87EA7/tmp/trim.6D37DFD2-5F27-4AB9-B478-5FED8AA6ABD7.MOV
// self.url = file:///var/mobile/Containers/Data/Application/066BDB03-FD47-48D8-B6F8-932AFB174DF7/Documents/AV/0b0b6803e891780850152eeab450a2ae.mov
do {
try fileManager.moveItem(at: sourceURL, to: self.url)
} catch {
QLogTools.logError("Error moving video clip file \(error)")
}
Error
Error Domain=NSCocoaErrorDomain Code=513 "“trim.90A10B33-B884-419F-BAD9-65583531C3C3.MOV” couldn’t be moved because you don’t have permission to access “AV”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Data/PluginKitPlugin/0849234B-837C-43ED-BEDD-DE4F79E7CE96/tmp/trim.90A10B33-B884-419F-BAD9-65583531C3C3.MOV, NSUserStringVariant=(
Move
I tried altering the permission of the folder I created in the first place to 0o777, but when I print out its attributes it returns 511 as its permissions.
P.S:This only happens on iOS 13.
I solved my issue, but it may be different to what you were experiencing: I still had issues when changing to copyItem. I'm adding my findings here as an answer in case anyone else stumbles upon this question as I did and it helps them out.
My app loads custom files that can be downloaded from my website. With iOS 13's download manager, these are copied to the Downloads folder first, and can then be opened by my app. Yet my app was having permission errors when attempting to access these files in the same way as iOS 12.
This answer to a different question clued me in. I need to call startAccessingSecurityScopedResource() and stopAccessingSecurityScopedResource() as follows:
// source and destination are URLs
if source.startAccessingSecurityScopedResource() {
try FileManager.default.moveItem(at: source, to: destination)
}
source.stopAccessingSecurityScopedResource()
The Apple documentation doesn't really suggest that this is new with iOS 13 but I think just the different approach to file downloading means sandboxing considerations need to be made.

How to download a file in iOS (Ionic 2/3)?

I am trying to download a file and save that file in iOS. I am using ionic-native-file plugin to achieve this. The file has been download but i could not find that file in the device.
filewrite = (): void => {
let transfer = this.fileTransfer.create();
let path = "";
let dir_name = 'Download';
let file_name = "Sample.pdf";
if (this.platform.is('ios')) {
this.platform.ready().then(() => {
path = this.file.documentsDirectory;
this.file.writeFile(path,file_name,this.pdfSrc).then((entry) => {
this.showAlert("download completed");
}, (error) => {
this.showAlert("Download Failed.");
}).catch(err=>{
this.showAlert("Download Failed catch.");
this.showAlert(err.message);
});
}
)
}
};
Download completed alert shows and the downloaded path is:
file:///var/mobile/Containers/Data/Application/6DE22F30-5806-4393-830A-14C8A1F320BE/Library/Cloud/Sample.pdf
But i could not find that location in the device. Then, i google and saw here.
cordova.file.documentsDirectory - Files private to the app, but that
are meaningful to other application (e.g. Office files). Note that for
OSX this is the user's ~/Documents directory. (iOS, OSX)
So, i can't see the file actually as it is private to the application. Then i saw in the same link:
So, i tried with the both preference in my config.xml but nothing happened.
Is there any approach to download the file iOS or to dropbox or anywhere else?
I've struggled to find the right directory to save to on different platforms with this cordova plugin too. I landed on using file.externalRootDirectory on Android, and file.cacheDirectory on iOS.
The right location likely depends on what you intend to do with the file. In my case I just needed it stored short-term so I can open it using the user's native PDF reader.

Resources