I've saved the file i want to share locally using FileSystem.downloadAsync
Share.share works fine for iOS. How can I share an image I have saved locally on Android?
I've tried
https://github.com/lucasferreira/react-native-send-intent
https://github.com/react-native-community/react-native-share
Both these solutions do not seem to work with Expo.
I'm using react-native version : https://github.com/expo/react-native/archive/sdk-31.0.0.tar.gz
FileSystem.downloadAsync(url, FileSystem.documentDirectory+filename).then(({uri})=>{
if(Platform.OS == "android"){
// ???
}
else{
Share.share({url:uri});
}
})
Is there something i'm missing?
Since SDK33, you can use Expo Sharing to share any type of file to other apps that can handle its file type even if you're on Android.
See : https://docs.expo.io/versions/latest/sdk/sharing/
Usage is pretty simple :
import * as Sharing from 'expo-sharing'; // Import the library
Sharing.shareAsync(url) // And share your file !
In order for users to share content saved within our (Expo) app, we structured it like this. (This is working across iOS & Android).
IMPORT SHARING:
import * as FileSystem from 'expo-file-system';
import * as Sharing from 'expo-sharing';
ADD ONPRESS TO BUTTON (OR WHEREVER):
<Button
name="share"
onPress={() =>
openShareDialogAsync(media, {
video: media.meta.fileType === 'video',
})
}
/>
SHARE VIDEO OR IMAGE TO ANY APP IN USERS HANDSET
const openShareDialogAsync = async (mediaProp, options) => {
const fileDetails = {
extension: options.video ? '.mp4' : '.jpg',
shareOptions: {
mimeType: options.video ? 'video/mp4' : 'image/jpeg',
dialosTitle: options.video
? 'Check out this video!'
: 'Check out this image!',
UTI: options.video ? 'video/mp4' : 'image/jpeg',
},
};
const downloadPath = `${FileSystem.cacheDirectory}${mediaProp.media_id}${fileDetails.extension}`;
const { uri: localUrl } = await FileSystem.downloadAsync(
mediaProp.url,
downloadPath
);
if (!(await Sharing.isAvailableAsync())) {
showMessage({
message: 'Sharing is not available',
description: 'Your device does not allow sharing',
type: 'danger',
});
return;
}
await Sharing.shareAsync(localUrl, fileDetails.shareOptions);
};
Hope this helps :]
Related
I'm using react-native-share to share content on instagram stories.
On iOS, it works fine; however, on Android, it does not resolve the promise and does not display any error.
import { default as RNShare } from "react-native-share";
const shareOnInstagram = async (callback) => {
const imageUrl =
"https://images.unsplash.com/photo-1556035511-3168381ea4d4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1674&q=80";
/* Download image using expo-file-system */
const base64 = await downloadImage({ url: imageUrl, isBase64: true });
const shareOptions = {
backgroundImage: imageUrl,
stickerImage: "data:image/png;base64," + base64,
backgroundBottomColor: "#fefefe",
backgroundTopColor: "#906df4",
social: RNShare.Social.INSTAGRAM_STORIES,
appId: META_API_KEY,
};
RNShare.shareSingle(shareOptions).catch((err) => {
console.log(err);
});
callback();
};
Here's the specs:
"react-native": "^0.70.6"
"react-native-share": "^8.0.1"
I think the problem in your Image format https://images.unsplash.com/photo-1556035511-3168381ea4d4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1674&q=80 - if you check it is image/avif but from android documentation https://developers.facebook.com/docs/instagram/sharing-to-stories/ you can find that it should be MEDIA_TYPE_JPEG. Try to use JPEG as a workaround and if it works probably you need to convert avif to jpeg for Android version
I'm trying to save a base64 encoded image in iOS using react-native-share and also Share module from React Native. But both fail when trying the Save Image option.
React Native Share
try {
const sharedResponse = await Share.open({ url: dataUri });
console.log(sharedRes);
} catch (error) {
console.log(error);
}
Share Module
try {
const sharedResponse = await Share.share({ url: dataUri });
console.log(sharedRes);
} catch (error) {
console.log(error);
}
Options other than Save image such as copy, and save to files are working fine.
I have added the following in Info.plist as well
<key>NSPhotoLibraryAddUsageDescription</key>
<string>APP wants to save to photos</string>
This is working fine on the first try in the app's lifetime (When it's asking the permissions from the user). After that this functionality doesn't work.
For some reason you need to write the file to the temp directory first before sharing. I'm not sure the reasoning behind this... but it did fix the issue for me.
const filename = `snapshot.jpeg`; // or some other way to generate filename
const filepath = `${FileSystem.cacheDirectory}/${filename}`;
await FileSystem.writeAsStringAsync(filepath, res.data, { encoding: 'base64' });
const isSharingAvailable = await isAvailableAsync();
if (!isSharingAvailable) {
showAlert('Error', 'Sharing is not available.')
return;
}
if (Platform.OS === 'ios') {
//sharing just the file allows for more applications to be shared too. Adding a message seems to remove many apps from the sharing list
await Share.share({ url: filepath });
}
This strange behaviour had happened because I'm trying to open the Share pop-up above a React Native Modal. The issue didn't occur if I try to hide the Modal before the Share pop-up comes up.
I resolved the issue when storing the image locally before opening the Share Modal.
To store the image i used the npm package 'react-native-fs' and then use it just like this:
import RNFS from "react-native-fs";
function storeFileLocally(url: string): Promise<string> {
const localFile = `${RNFS.DocumentDirectoryPath}/tempFile.jpeg`;
const options: RNFS.DownloadFileOptions = {
fromUrl: url,
toFile: localFile
};
return RNFS.copyFile(url, localFile)
.then(() => 'file://'+localFile)
.catch((error) => {
console.error(error);
return null;
});
}
Unable to save pdf file on android which is created using PDFMake library.
In my ionic app, I am not able to download pdf which I created using PDFMake library even it log success. Here I am trying to writeFile using file module, when user click on generatePdf button
Since last two days I am trying to achieve this.. but no luck, so now I am looking for some help here.
component.html
<ion-button (click)="generatePdf()">generatePdf</ion-button>
component.ts
generatePdf() {
let self = this;
const pdfData = {
content: [
'First paragraph',
'Another paragraph',
],
};
let pdfDocGenerator = pdfMake.createPdf(pdfData);
pdfDocGenerator.getBuffer((buffer) => {
let utf8 = new Uint8Array(buffer);
let binaryArray = utf8.buffer;
self.saveToDevice(binaryArray, 'first.pdf');
});
}
saveToDevice(data: any, savefile: any) {
let self = this;
self.file.writeFile(self.file.externalApplicationStorageDirectory, savefile, data, { replace: false
})
.then(() => {
console.log('success...');
})
.catch((err) => {
console.log('error...');
});
}
I am trying to implement a feature to let the user upload a file in my NativeScript Angular Project. NativeScript does not seem to have a native implementation of a file picker and there are limited plugins available that can do the job. Plus they have their own set of problems. The closest I have come to a workable solution is using the nativescript-mediafilepicker and that opens a blank page like the one below instead of the file explorer.
I exactly followed the documentation and can't figure out why it's not working. Here is the service I wrote:
payload.service.ts
import { Injectable } from '#angular/core';
import { Mediafilepicker, ImagePickerOptions, VideoPickerOptions, AudioPickerOptions,
FilePickerOptions } from 'nativescript-mediafilepicker';
#Injectable({
providedIn: 'root'
})
export class PayloadService {
constructor() { }
pickFile(){
console.log('Pick File Payload Service requested');
const extensions = ['pdf'];
let options: FilePickerOptions = {
android: {
extensions: extensions,
maxNumberFiles: 1
},
ios: {
extensions: extensions,
multipleSelection: false
}
};
let mediafilepicker = new Mediafilepicker();
mediafilepicker.openFilePicker(options);
mediafilepicker.on("getFiles", function (res) {
let results = res.object.get('results');
console.dir('File Pick Success: ',results);
});
mediafilepicker.on("error", function (res) {
let msg = res.object.get('msg');
console.log('File Pick Error: ',msg);
});
mediafilepicker.on("cancel", function (res) {
let msg = res.object.get('msg');
console.log('File Pick Cancel: ',msg);
});
}
}
Can someone help me fix this or rather provide me with a native implementation? I don't need much customization options and user will only upload one file at a time.
I am building an ionic app for wallpapers.
In the app,there is an image stored in www/assets/img displayed.I have build 2 buttons below,for downloading and retrieving the displayed image to the mobile device memory.
When i click download button,a dialog is shown,saying "Download Succeeded!Pug.jpg was successfully downloaded to: filepath".But when i check the phone memory no such file is there.Also when i click "Retrieve"Button it's showing dialog saying"File retrieval succeed!Pug.jpg was successfully retrieved from: filepath""even though file is not present in the phone memory.
This is home.ts code
import {Component} from '#angular/core';
import {NavController, Platform, AlertController} from 'ionic-angular';
import {Transfer, TransferObject} from '#ionic-native/transfer';
import {File} from '#ionic-native/file';
declare var cordova: any;
#Component({
selector: 'page-home',
templateUrl: 'home.html',
providers: [Transfer, TransferObject, File]
})
export class HomePage {
storageDirectory: string = '';
constructor(public navCtrl: NavController, public platform: Platform, private transfer: Transfer, private file: File, public alertCtrl: AlertController) {
this.platform.ready().then(() => {
// make sure this is on a device, not an emulation (e.g. chrome tools device mode)
if(!this.platform.is('cordova')) {
return false;
}
if (this.platform.is('ios')) {
this.storageDirectory = cordova.file.documentsDirectory;
}
else if(this.platform.is('android')) {
this.storageDirectory = cordova.file.dataDirectory;
}
else {
// exit otherwise, but you could add further types here e.g. Windows
return false;
}
});
}
downloadImage(image) {
this.platform.ready().then(() => {
const fileTransfer: TransferObject = this.transfer.create();
const imageLocation = `${cordova.file.applicationDirectory}www/assets/img/${image}`;
fileTransfer.download(imageLocation, this.storageDirectory + image).then((entry) => {
const alertSuccess = this.alertCtrl.create({
title: `Download Succeeded!`,
subTitle: `${image} was successfully downloaded to: ${entry.toURL()}`,
buttons: ['Ok']
});
alertSuccess.present();
}, (error) => {
const alertFailure = this.alertCtrl.create({
title: `Download Failed!`,
subTitle: `${image} was not successfully downloaded. Error code: ${error.code}`,
buttons: ['Ok']
});
alertFailure.present();
});
});
}
retrieveImage(image) {
this.file.checkFile(this.storageDirectory, image)
.then(() => {
const alertSuccess = this.alertCtrl.create({
title: `File retrieval Succeeded!`,
subTitle: `${image} was successfully retrieved from: ${this.storageDirectory}`,
buttons: ['Ok']
});
return alertSuccess.present();
})
.catch((err) => {
const alertFailure = this.alertCtrl.create({
title: `File retrieval Failed!`,
subTitle: `${image} was not successfully retrieved. Error Code: ${err.code}`,
buttons: ['Ok']
});
return alertFailure.present();
});
}
}
This is home.html code
<ion-header>
<ion-navbar>
<ion-title>
File Transfer Example
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-card>
<ion-card-header>
Ionic 3 File Transfer Example
</ion-card-header>
<ion-card-content>
<img src="assets/img/pug.jpg" alt="Cute Pug">
<button ion-button (click)="downloadImage('pug.jpg')" color="secondary">Download image</button>
<button ion-button (click)="retrieveImage('pug.jpg')" color="secondary">Retrieve downloaded image</button>
</ion-card-content>
</ion-card>
</ion-content>
I build this ionic app based on this Github code example
I actually want the ionic app to first create a folder(app named folder) in internal memory and put all images there.So users can access files in that folder.For example,if app name is "Appsample" then all images should be in Appsample folder in internal memory.
How can i develop for above purpose?
Thanks.
I just posted an answer to nearly the same question, see:
Download not working using filetransfer plugin.
The main problem here is that you are using the following directory to save your file:
else if(this.platform.is('android')) {
this.storageDirectory = cordova.file.dataDirectory;
}
As stated in the cordova docs (https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/#where-to-store-files), "cordova.file.dataDirectory" is the persistent and private data storage within the application's sandbox using internal memory.
Use cordova.file.externalDataDirectory to fit your purpose. Then the file should be placed somewhere here: "file:///storage/emulated/0/Android/data/subdomain.domainname.toplevdomain/files/...".
On Android, external storage directories always exist. If the device doesn't have a physical card, Android will emulate it.
You can use this way, it's working file
Capacitor
npm install cordova-plugin-whitelist
npx cap sync
Cordova
npm install cordova-plugin-whitelist
npx cap update
download() {
const url = 'http://www.example.com/file.pdf';
fileTransfer.download(url, this.file.dataDirectory + 'file.pdf').then((entry) => {
console.log('download complete: ' + entry.toURL());
}, (error) => {
// handle error
});
}
I Have the same issue when i wrote my download code it worked but i couldn't see the file on my phone
1. Write download method
2. Write Permission method, Call Permission method
3. Call download method inside Permission, the phone will pop up and ask for permission for file read if it has not been set.
4. After download you might not be able to see where the file is on phone, you will now use photoViewer to view it on phone if it is
image or use document viewer if it is pdf or other document related.
The plugins needed
private transfer: FileTransfer,
private fileTransfer: FileTransferObject,
private file: File,
private androidPermissions: AndroidPermissions,
private photoViewer: PhotoViewer,
getPermission() {
// get permission from device to save
this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE).then(
result =>//,
err => this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE)
);
// get permission from device to save
this.androidPermissions.hasPermission(this.androidPermissions.PERMISSION.WRITE_EXTERNAL_STORAGE)
.then(status => {
if (status.hasPermission) {
this.download('k');
}
else {
this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.WRITE_EXTERNAL_STORAGE)
.then(status => {
if (status.hasPermission) {
this.download('');
}
});
}
});
}
public download() {
let url = encodeURI(this.imgUrl);
var imagePath = this.file.dataDirectory + "myDP.png";
const fileTransfer = this.transfer.create();
fileTransfer.download(url, imagePath).then((entry) => {
this.generalProvider.showToast('download completed: ' + imagePath);
this.photoViewer.show(entry.toURL());
}, (error) => {
});
}