Cant download on iOS with expo-file-system - ios

Im trying to download using Expo-File-System on an expo project with react native.
The download option works on Expo-Go during testing but wont work when i push it in production mode.
It says that it is "unable to save file in the local URI because you dont have Document Permissions" as shown in the Picture below.
Here is the code (Using typescript):
import * as MediaLibrary from 'expo-media-library';
///For downloading the file
const download = async (item: ICurriculumCourseDocument) => {
setIsloading(true)
await FileSystem.downloadAsync(
`https:/baseUrl`,
FileSystem.documentDirectory + item.filename,
options
)
.then(({ uri }) => {
setIsloading(false)
console.log('Finished downloading to', uri);
onShareIOS(uri)
})
.catch(error => {
setIsloading(false)
alert(error);
});
}
//For sharing the file using the Share
const onShareIOS = async (uri: string) => {
const imageFileExts = ['jpg', 'png', 'gif', 'heic', 'webp', 'bmp'];
if (imageFileExts.every(x => !uri.endsWith(x))) {
const UTI = 'public.item';
await Sharing.shareAsync(uri, {UTI});
}
};
Here is the full Alert with the error message:

Related

ReactJS: Download file to IOS device (file-saver and firebase)

I am using the following in an attempt to download a document onto the user's device. This document has been uploaded to Firebase Storage.
import { saveAs } from 'file-saver';
const handleDownload = (url, filename) => {
axios.get(url, {
responseType: 'blob',
})
.then((res) => {
// fileDownload(res.data, filename)
saveAs(url)
})
}
The URL looks something like:
https://firebasestorage.googleapis.com/v0/b/appspot.com/o/rWFZhAKk9eOSIIFoP0DqqvrC6WJ3%2F2022-07-31%2FXXX.pdf?alt=media&token=9ea477b5-4371-4369-947a-368bc01043d5
And then calling this via a onClick (as per docs)
<IconButton size="small" aria-label="download" onClick={() => {handleDownload(el.url, el.name)}}><CloudDownloadIcon /></IconButton>
However on IOS I get no response, no download and no error in console.
Is this something with Firebase that I need to correct, or am I doing something else wrongly? If I open that URL from the app, then it loads, but I want to give the user the option to download.

flutter_downloader doesn't show notification of downloading in iOS

I use flutter_downloader in app to allow downloading pdf files .
It is working well in Android,but in IOS,the notification of starting downloading the file doesn't appear although the file downloaded in the device.
I followed all instructions like described from Enable background mode,Add sqlite library,Configure AppDelegate
code:
final status = await Permission.storage.request();
if (status.isGranted) {
// Directory _path = await getExternalStorageDirectory();
// String _localPath = _path.absolute.path + Platform.pathSeparator + 'ESPRS_Docs';
var savedDir;
if (Platform.isIOS) {
savedDir = await getApplicationDocumentsDirectory();
} else {
savedDir = Directory('/storage/emulated/0/Download');
// Put file in global download folder, if for an unknown reason it didn't exist, we fallback
// ignore: avoid_slow_async_io
if (!await savedDir.exists()) savedDir = await getExternalStorageDirectory();
}
String random_no =randomAlphaNumeric(6) ;
var split_name=p_title.replaceAll(" ","-");
await FlutterDownloader.enqueue(
url: p_link,
savedDir: savedDir.path,
fileName: "${split_name}-${random_no}.pdf",
showNotification: true,
saveInPublicStorage: true,
openFileFromNotification: true,
headers: {"auth": "Downloader"},
);
}else {
print("Permission deined");
};
Notification messages of download progress are only shown for Android. Behaviour differs between platforms.
final taskId = await FlutterDownloader.enqueue(
url: 'your download link',
savedDir: 'the path of directory where you want to save downloaded files',
showNotification: true, // show download progress in status bar (for Android)
openFileFromNotification: true, // click on notification to open downloaded file (for Android)
);

Is there any way to track an event using firebase in electron + react

I want to ask about how to send an event using firebase & electron.js. A friend of mine has a problem when using firebase analytics and electron that it seems the electron doesn't send any event to the debugger console. When I see the network it seems the function doesn't send anything but the text successfully go in console. can someone help me to figure it? any workaround way will do, since he said he try to implement the solution in this topic
firebase-analytics-log-event-not-working-in-production-build-of-electron
electron-google-analytics
this is the error I got when Try to use A solution in Point 2
For information, my friend used this for the boiler plate electron-react-boilerplate
The solution above still failed. Can someone help me to solve this?
EDIT 1:
As you can see in the image above, the first image is my friend's code when you run it, it will give a very basic example like in the image 2 with a button to send an event.
ah just for information He used this firebase package :
https://www.npmjs.com/package/firebase
You can intercept HTTP protocol and handle your static content though the provided methods, it would allow you to use http:// protocol for the content URLs. What should make Firebase Analytics work as provided in the first question.
References
Protocol interception documentation.
Example
This is an example of how you can serve local app as loaded by HTTP protocol and simulate regular browser work to use http protocol with bundled web application. This will allow you to add Firebase Analytics. It supports poorly HTTP data upload, but you can do it on your own depending on the goals.
index.js
const {app, BrowserWindow, protocol} = require('electron')
const http = require('http')
const {createReadStream, promises: fs} = require('fs')
const path = require('path')
const {PassThrough} = require('stream')
const mime = require('mime')
const MY_HOST = 'somehostname.example'
app.whenReady()
.then(async () => {
await protocol.interceptStreamProtocol('http', (request, callback) => {
const url = new URL(request.url)
const {hostname} = url
const isLocal = hostname === MY_HOST
if (isLocal) {
serveLocalSite({...request, url}, callback)
}
else {
serveRegularSite({...request, url}, callback)
}
})
const win = new BrowserWindow()
win.loadURL(`http://${MY_HOST}/index.html`)
})
.catch((error) => {
console.error(error)
app.exit(1)
})
async function serveLocalSite(request, callback) {
try {
const {pathname} = request.url
const filepath = path.join(__dirname, path.resolve('/', pathname))
const stat = await fs.stat(filepath)
if (stat.isFile() !== true) {
throw new Error('Not a file')
}
callback(
createResponse(
200,
{
'content-type': mime.getType(path.extname(pathname)),
'content-length': stat.size,
},
createReadStream(filepath)
)
)
}
catch (err) {
callback(
errorResponse(err)
)
}
}
function serveRegularSite(request, callback) {
try {
console.log(request)
const req = http.request({
url: request.url,
host: request.url.host,
port: request.url.port,
method: request.method,
headers: request.headers,
})
if (req.uploadData) {
req.write(request.uploadData.bytes)
}
req.on('error', (error) => {
callback(
errorResponse(error)
)
})
req.on('response', (res) => {
console.log(res.statusCode, res.headers)
callback(
createResponse(
res.statusCode,
res.headers,
res,
)
)
})
req.end()
}
catch (err) {
callback(
errorResponse(err)
)
}
}
function toStream(body) {
const stream = new PassThrough()
stream.write(body)
stream.end()
return stream
}
function errorResponse(error) {
return createResponse(
500,
{
'content-type': 'text/plain;charset=utf8',
},
error.stack
)
}
function createResponse(statusCode, headers, body) {
if ('content-length' in headers === false) {
headers['content-length'] = Buffer.byteLength(body)
}
return {
statusCode,
headers,
data: typeof body === 'object' ? body : toStream(body),
}
}
MY_HOST is any non-existent host (like something.example) or host that is controlled by admin (in my case it could be electron-app.rumk.in). This host will serve as replacement for localhost.
index.html
<html>
<body>
Hello
</body>
</html>

IOS Safari issue of download file name is "Unknown"

I implemented a Jersey REST service to download the zip file.
Now, I would like to use axios in front end to download the zip file.
Everything is fine in PC Chrome but when tried with Safari on iPad it opens a tab with name "unknown".
I have searched some articles and mentioned that this may related to IOS safari compatibility.
e.g. https://caniuse.com/#feat=download
However, I also want know if there is any method to show the downloaded file as "file.zip" for safari.
Below is my code
Backend:
#GET
#Path("/getTestingReport")
#Produces("application/zip")
public Response getTestingReport() throws Exception {
// set file (and path) to be download
File file = new File("C:/Users/abc/Desktop/test.zip");
ResponseBuilder responseBuilder = Response.ok((Object) file);
responseBuilder.header("Content-Disposition", "attachment; filename=\"MyJerseyZipFile.zip\"");
return responseBuilder.build();
}
Frontend:
axios.get("report/getTestingReport").then((response) => {
console.log("response", response)
var blob = new Blob([response.data], { type: "application/zip" });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip');
document.body.appendChild(link);
link.click();
}).catch((error) => {
console.error("error response", error.response)
});
May I have any suggestion?

Is there a way to upload media to the Twitter API using the URL of the media resource?

I've created an app that allows users to upload their images to a Google Cloud Storage bucket - which is then used in social media sharing previews.
The image is uploaded directly to the bucket from the user's browser - using the Firebase API.
What I also want to do is - when an image is saved - to automatically post that image on my app's twitter feed.
The way I've done this is to use a Cloud Function trigger on Cloud Storage - which downloads the image and then uploads via the Twitter API.
There's essentially an unnecessary double handling of traffic here is there a way to just give the Twitter API the public location of the file and have it source the file directly?
Here's my code for the current solution:
class Defferred {
constructor() {
const that = this;
this.prom = new Promise((resolve, reject) => {
that.resolve = resolve;
that.reject = reject;
});
}
}
exports.onNewImage = functions.storage.object().onFinalize((object) => {
const prom = new Defferred();
bucket.file(object.name).download((err, file, response) => {
if (err) {
return prom.reject(err);
} else {
twitterClient.post('media/upload', {
media: file
}, (err, media, response) => {
if (!err) {
let status = {
status: "Somebody created this at https://geoplanets.io #geometry #geometricart",
media_ids: media.media_id_string
}
twitterClient.post('statuses/update', status, (error, tweet, response) => {
if (!error) {
return prom.resolve(response);
} else {
return prom.reject(error);
}
});
} else {
return prom.reject(err);;
}
});
}
});
return prom.prom;
});
Is there an alternative way of doing this that doesn't involve downloading the file? - A good answer would highlight the relevant parts of the API documentation that highlight how I would go about working this out myself.
The Twitter node api doesn't have a way to simply pass an URL for media upload. The example they give shows what you're doing now - sending the full content with the request.
The node client is just a wrapper around the REST API, and if you read its docs, you'll see that you have to provide the file content directly to the POST.
Yes!
We can upload media using URL of file by making the downstream of a file.
First we need to make Axios request to have a buffer of it then we can pass it with file type using
twitter-api-v2
use it in this package or REST API
const client = new TwitterApi({
appKey: CONSUMER_KEY,
appSecret: CONSUMER_SECRET,
accessToken: oauth_token,
accessSecret: oauth_token_secret,
});
const url = 'URL OF THE FILE';
const downStream = await axios({
method: 'GET',
responseType: 'arraybuffer',
url: url,
}).catch(function (error) {
res.send({error:error});
});
const mediaId = await client.v1.uploadMedia(downStream.data,{ mimeType: 'png'});
const newTweet = await client.v1.tweet('Hello link tweet!', { media_ids: mediaId });
sample Image

Resources