Cordova Ionic Native File plugin - Where does it store the files in iOS? - ionic-framework

I been trying to save a file on iOS. Right now I been running through Xcode right to my phone.
When I run the app it says that is saved successfully. But I don’t see any file when I use a file manager app like FileApp, FileManager, iCloud and the Apple crappy Files app.
My question is that I heard from a web search that in order to save the file iOS creates a sandbox folder for the app.
If I been saving it to this.file.documentDirectory, how can a user open it in let’s say Pages or Numbers apps? (You know Apple’s Word and Excel replacement for the uninitiated.)
Here’s the code.
writeToIOS(opts : {
fileName: string,
text: any
}) : Promise < IResponse < any >> {
let response: IResponse < boolean > = {
success: false,
error: '',
data: false
};
const options: IWriteOptions = {
replace: true
};
return new Promise((resolve, reject) => {
const path = this.file.documentsDirectory;
const directory = 'Attendance Log';
this
.file
.checkDir(path, directory)
.then(res => {
this
.file
.writeFile(path + directory, opts.fileName, opts.text, options)
.then(res => {
response = {
...response,
success: true,
error: res,
data: res
};
resolve(response);
})
.catch(error => reject(error));
})
.catch(() => {
this
.file
.createDir(path, directory, true)
.then(directory => {
this
.file
.writeFile(path + directory.name, opts.fileName, opts.text, options)
.then(res => {
response = {
...response,
success: true,
error: res,
data: res
};
resolve(response);
})
.catch(error => reject(error));
})
.catch(error => reject(error));
});
});
}
EDIT:
What the client/user should do is be able to choose the file and open it in his/her favorite app. That being a File Manager app or a Writer or Spreadsheet app.

I don't have too much experience with iOS, but I think you have chosen good dir.
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)
In iOS i would search /var/mobile/Applications/<UUID>/Documents

This is using the cordova-plugin-fileopener2 in conjuction with ionic native file opener:
const options = {
replace: true
};
const path = this.file.documentsDirectory;
const directory = 'Attendance Log';
this.file
.checkDir(path, directory)
.then(res => {
this.file
.writeFile(path + directory, opts.fileName, opts.text, options)
.then(res => {
this.fileOpener
.open(`${path}${directory}/${opts.fileName}` , 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
.then(() => ...)
.catch(error => ...);
})
.catch(error => ...);

Related

How to solve File (.pdf) path directory doesn't exist error if I select file (.pdf) from mobile download folder in ionic

I want to upload the pdf file on our server using the file-chooser Cordova plugin. I had success in the file selection but after the file selection I tried to upload on the server but received the error "directory doesn't exist".
var filter = { "mime": "application/pdf" };
this.fileChooser.open(filter)
.then(url => {
console.log('fileChooser:',url);
this.filePath.resolveNativePath(url)
.then(filePath => {
console.log('saif file pathhh',filePath);
let correctPath = url.substr(0,url.lastIndexOf("/") + 1);
let currentName = filePath.substring(filePath.lastIndexOf('/') + 1);
this.file.checkDir(this.file.externalDataDirectory, correctPath).then(_ => console.log('Directory exists')).catch(err =>
console.log('Directory doesnt exist'));
let _filePath = filePath;
let resPath = this.pathForImage(_filePath);
let newEntry = {
name: currentName,
path: resPath,
filePath: _filePath,
comparessedPath: _filePath,
};
this.startUpload(newEntry, index, field_id);
})
.catch(err => console.log('saif file pathhh errorrr',err));
})
.catch(e => {
console.log('saif file pathhh errorrr catchhh',e);
});
I want to real path of selected pdf file by using file-chooser cordova plugin

Facebook photo upload date timestamp

I've downloaded all my Facebook data and wish to upload some of the images that I've sent via Messenger to Google Photos. I wish to have them to have the correct metadata so they are uploaded under the correct day, not under today. Unfortunately, they have the date of download for Date created.
I tried parsing the title, but it doesn't seem to be a timestamp.
My question is: is there a way to create a script that adds the correct metadata to a photo downloaded from Facebook (via Download your information archive)? An example title is: 142666616_209126620919024_535058535265435125_n.jpg. This photo should have the date Jan 27, 2021, 10:53 AM.
After some digging I found a solution.
The archive that Facebook gives you has folders for each friend with the following structure:
\friend_name_a1b2c3
\photos
12345678_123456788996_123124421.jpg
\gifs
\audio
messages_1.json
messages_1.json has all your messages with that friend and here is an example how a message looks like:
{
"sender_name": "Your Name",
"timestamp_ms": 1562647443588,
"photos": [
{
"uri": "messages/inbox/friend_name_a1b2c3/photos/12345678_123456788996_123124421.jpg",
"creation_timestamp": 1562647443
}
],
"type": "Generic",
"is_unsent": false
},
So, using glob and utimes I came up with the following script:
var glob = require("glob")
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require("fs"));
var { utimes } = require("utimes");
const readJSONFiles = async () => {
const messagesFiles = glob.sync(`**/message_*.json`)
const promises = [];
messagesFiles.forEach(mFile => {
promises.push(fs.readFileAsync(mFile, 'utf8'));
})
return Promise.all(promises);
}
readJSONFiles().then(result => {
const map = {};
result.forEach(data => {
const messagesContents = JSON.parse(data);
messagesContents.messages
.filter(m => m.photos)
.forEach(m => {
m.photos.filter(p => {
const splitted = p.uri.split("/")
const messagePhotoFileName = splitted[splitted.length - 1];
map[messagePhotoFileName] = m.timestamp_ms;
})
})
})
fs.writeFileSync("./map.json", JSON.stringify(map))
}).then(() => {
fs.readFileAsync("./map.json", 'utf8').then(data => {
const map = JSON.parse(data);
glob("**/*.jpg", function (er, files) {
files.forEach(file => {
const [, , photo] = file.split("/");
utimes(file, {
btime: map[photo],
atime: map[photo],
mtime: map[photo]
});
})
})
})
});
It creates a map of file-name:date-taken then loops over all .jpg files and changes its metadata. It definitely is a little rough around the edges but gets the job done, after all.

Image returned from REST API always displays broken

I am building a content management system for an art portfolio app, with React. The client will POST to the API which uses Mongoose to insert into a MongoDB. The API then queries the DB for the newly inserted image, and returns it to the client.
Here's my code to connect to MongoDB using Mongoose:
mongoose.connect('mongodb://localhost/test').then(() =>
console.log('connected to db')).catch(err => console.log(err))
mongoose.Promise = global.Promise
const db = mongoose.connection
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
const Schema = mongoose.Schema;
const ImgSchema = new Schema({
img: { data: Buffer, contentType: String }
})
const Img = mongoose.model('Img', ImgSchema)
I am using multer and fs to handle the image file. My POST endpoint looks like this:
router.post('/', upload.single('image'), (req, res) => {
if (!req.file) {
res.send('no file')
} else {
const imgItem = new Img()
imgItem.img.data = fs.readFileSync(req.file.path)
imgItem.contentType = 'image/png'
imgItem
.save()
.then(data =>
Img.findById(data, (err, findImg) => {
console.log(findImg.img)
fs.writeFileSync('api/uploads/image.png', findImg.img.data)
res.sendFile(__dirname + '/uploads/image.png')
}))
}
})
I can see in the file structure that writeFileSync is writing the image to the disk. res.sendFile grabs it and sends it down to the client.
Client side code looks like this:
handleSubmit = e => {
e.preventDefault()
const img = new FormData()
img.append('image', this.state.file, this.state.file.name)
axios
.post('http://localhost:8000/api/gallery', img, {
onUploadProgress: progressEvent => {
console.log(progressEvent.loaded / progressEvent.total)
}
})
.then(res => {
console.log('responsed')
console.log(res)
const returnedFile = new File([res.data], 'image.png', { type: 'image/png' })
const reader = new FileReader()
reader.onloadend = () => {
this.setState({ returnedFile, returned: reader.result })
}
reader.readAsDataURL(returnedFile)
})
.catch(err => console.log(err))
}
This does successfully place both the returned file and the img data url on state. However, in my application, the image always displays broken.
Here's some screenshots:
How to fix this?
Avoid sending back base64 encoded images (multiple images + large files + large encoded strings = very slow performance). I'd highly recommend creating a microservice that only handles image uploads and any other image related get/post/put/delete requests. Separate it from your main application.
For example:
I use multer to create an image buffer
Then use sharp or fs to save the image (depending upon file type)
Then I send the filepath to my controller to be saved to my DB
Then, the front-end does a GET request when it tries to access: http://localhost:4000/uploads/timestamp-randomstring-originalname.fileext
In simple terms, my microservice acts like a CDN solely for images.
For example, a user sends a post request to http://localhost:4000/api/avatar/create with some FormData:
It first passes through some Express middlewares:
libs/middlewares.js
...
app.use(cors({credentials: true, origin: "http://localhost:3000" })) // allows receiving of cookies from front-end
app.use(morgan(`tiny`)); // logging framework
app.use(multer({
limits: {
fileSize: 10240000,
files: 1,
fields: 1
},
fileFilter: (req, file, next) => {
if (!/\.(jpe?g|png|gif|bmp)$/i.test(file.originalname)) {
req.err = `That file extension is not accepted!`
next(null, false)
}
next(null, true);
}
}).single(`file`))
app.use(bodyParser.json()); // parses header requests (req.body)
app.use(bodyParser.urlencoded({ limit: `10mb`, extended: true })); // allows objects and arrays to be URL-encoded
...etc
Then, hits the avatars route:
routes/avatars.js
app.post(`/api/avatar/create`, requireAuth, saveImage, create);
It then passes through some user authentication, then goes through my saveImage middleware:
services/saveImage.js
const createRandomString = require('../shared/helpers');
const fs = require("fs");
const sharp = require("sharp");
const randomString = createRandomString();
if (req.err || !req.file) {
return res.status(500).json({ err: req.err || `Unable to locate the requested file to be saved` })
next();
}
const filename = `${Date.now()}-${randomString}-${req.file.originalname}`;
const filepath = `uploads/${filename}`;
const setFilePath = () => { req.file.path = filepath; return next();}
(/\.(gif|bmp)$/i.test(req.file.originalname))
? fs.writeFile(filepath, req.file.buffer, (err) => {
if (err) {
return res.status(500).json({ err: `There was a problem saving the image.`});
next();
}
setFilePath();
})
: sharp(req.file.buffer).resize(256, 256).max().withoutEnlargement().toFile(filepath).then(() => setFilePath())
If the file is saved, it then sends a req.file.path to my create controller. This gets saved to my DB as a file path and as an image path (the avatarFilePath or /uploads/imagefile.ext is saved for removal purposes and the avatarURL or [http://localhost:4000]/uploads/imagefile.ext is saved and used for the front-end GET request):
controllers/avatars.js (I'm using Postgres, but you can substitute for Mongo)
create: async (req, res, done) => {
try {
const avatarurl = `${apiURL}/${req.file.path}`;
await db.result("INSERT INTO avatars(userid, avatarURL, avatarFilePath) VALUES ($1, $2, $3)", [req.session.id, avatarurl, req.file.path]);
res.status(201).json({ avatarurl });
} catch (err) { return res.status(500).json({ err: err.toString() }); done();
}
Then when the front-end tries to access the uploads folder via <img src={avatarURL} alt="image" /> or <img src="[http://localhost:4000]/uploads/imagefile.ext" alt="image" />, it gets served up by the microservice:
libs/server.js
const express = require("express");
const path = app.get("path");
const PORT = 4000;
//============================================================//
// EXPRESS SERVE AVATAR IMAGES
//============================================================//
app.use(`/uploads`, express.static(`uploads`));
//============================================================//
/* CREATE EXPRESS SERVER */
//============================================================//
app.listen(PORT);
What it looks when logging requests:
19:17:54 INSERT INTO avatars(userid, avatarURL, avatarFilePath) VALUES ('08861626-b6d0-11e8-9047-672b670fe126', 'http://localhost:4000/uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png', 'uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png')
POST /api/avatar/create 201 109 - 61.614 ms
GET /uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png 200 3027 - 3.877 ms
What the user sees upon successful GET request:

Ionic native file opener 2 error class not found

I receive error when trying to open a file Class not found
.ts
open(){
this.platform.ready().then(() => {
this.fileOpener.open(this.entry.nativeURL, this.attachment.mime).then(() => {
console.log('file opened')
}, err => {
console.log('error open file: ', err)
});
});
}
this.entry.nativeURL is the download result using File Transfer
nativeURL: file:///storage/emulated/0/Download/someFile.docx
this.fileTransfer.download(url, this.storageDirectory + this.attachment.fileName, trustAllHosts).then((entry) => {
console.log('entry: ', entry);
this.entry = entry;
}
this.attachment.mime: application/vnd.openxmlformats-officedocument.wordprocessingml.document
I also tried application/pdf for a pdf file , didn't work
Its working... the problem was in running the app live --livereload

Uploading files via sails v.9.16

I am trying to upload a bunch of files to the server with skipper and jquery-file-uploader in sails v.9.x. I also need to add in two other field names with the form and multiple files. But i'm having some trouble getting it to work quite right. When I log the server it says that the files aren't there. Could I get some help?
Here is my front-end js:
var filesList = [],
fileupload = $('#uploader'),
paramNames = [];
var button = $("button.success.start.uploadbutton");
file_upload = fileupload.fileupload({
autoUpload: false,
fileInput: $("input:file"),
}).on("fileuploadadd", function(e, data){
filesList.push(data.files[0]);
paramNames.push(e.delegatedEvent.target.name);
});
button.click(function(e){
e.preventDefault();
var data = fileupload.serialize();
var toServer = {
data: data, files:filesList, paramName: paramNames
};
console.log(file_upload.fileupload);
file_upload.fileupload('send', toServer
).success(function(result, textstatus, jqXHR){
console.log("gettting the file uploaded!");
});
});
and here is the server side:
'upload': function (req, res) {
req.file('files').upload(function (err, files) {
console.log(files);
if (err) return res.serverError(err);
return res.json({
message: files.length + ' file(s) uploaded successfully!',
files: files
});
});
and the express js:
module.exports.express = {
bodyParser: require('skipper')
}