Unable to generate excel file from XLSX in ionic - ios

I am creating excel file from user data but unfortunately its not generating file and even don't know what is the error so I can at-least try to solve that error.
Specially this issue occur in iOS platform only.
Please find below code to generate excel file:
public createXSLX(): Promise<any> {
return new Promise((resolve) => {
let sheet = XLSX.utils.json_to_sheet(this.data);
let wb = {
SheetNames: ["export"],
Sheets: {
"export": sheet
}
};
let wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
});
function s2ab(s) {
let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf);
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
let blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' });
resolve(blob);
});
}
This above function works very well in android but in iOS its not generation file from provided data.
How I call above function code :
onExportNew = function (toEmail) {
this.createXSLX().then((xclBlob) => {
let time = new Date().getTime();
let fileName: string = "roster_" + time + ".xlsx";
var fs = ''
if (this.platform.is('ios')){
fs = this.file.documentsDirectory;
}else{
fs = this.file.externalDataDirectory;
}
console.log("File Path:- ",fs)
this.file.writeFile(fs, fileName, xclBlob, true).then(() => {
let fp = fs + fileName;
let email = {
// to: 'lmahajan#cisco.com',
// cc: 'erika#mustermann.de',
// bcc: ['john#doe.com', 'jane#doe.com'],
to: toEmail,
attachments: [fp],
subject: 'Roster Excel File',
body: '<h1>PFA</h1>',
isHtml: true
};
this.emailComposer.open(email).then(() => {
this.showDone = true;
}).catch(() => {
let toast = this.toastCtrl.create({
message: 'Could not open email composer',
duration: 3000
});
toast.present();
});
}).catch(() => {
this.displayAlert('Error', 'error creating file at: ' + fs);
});
}).catch(() => {
console.log("Excel file creation error");
});
}
Guide me if missing anything in above code.
Thanks in advance!

Related

Image Upload from iOS to AWS S3 using Just, KOA/Nodejs; file path and buffer won't read properly

I am attempting to implement file storage in an S3-compatible blob store when a user selects a photo in an iOS mobile app. I am using the Just library on the mobile side for an authenticated POST request, and koa libraries on top of a nodejs server on the backend. Everything appears to be working perfectly when I watch the logs in xcode and on our server (ie: the photo's name, path, and type are exposed/provided in the POST request), but I receive an error in my server logs that the file/path does not exist when the S3 function is attempting to execute. I know this is a layered issue but any help greatly appreciated.
Swift Code (which seems to work perfectly):
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
      selectPhotoImageView.image = editedImage
       
      let imgUrl = info[UIImagePickerController.InfoKey.imageURL] as! URL
      let imgName = imgUrl.lastPathComponent
      let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
      let localPath = documentDirectory?.appending(imgName)
      let data = editedImage.jpegData(compressionQuality: 0.5)! as NSData
      data.write(toFile: localPath!, atomically: true)
      let photoURL = URL.init(fileURLWithPath: localPath!)
      let partFilename = photoURL.lastPathComponent
      if let URLContent = try? Data(contentsOf: photoURL) {
        let partContent = URLContent
      }
       
      let resp = SessionManager.shared.just()?.post(
        "https://app.myta.io/api/1/file-upload",
        data: ["path": photoURL,
            "filename": "testimage"],
        files: ["file": .url(photoURL, "image/jpeg")]
      )
      if (resp != nil && resp!.ok) {
        print("Made successful request")
      } else {
        print("Request failed :(((")
      }
}
}
Backend Code:
import { authenticateJWT, checkAuthenticated } from 'server/auth';
import { Context, DefaultState } from 'koa';
import Koa from 'koa';
import mount from 'koa-mount';
import Router from 'koa-router';
import { addUploadedFile } from 'server/external/file_upload';
import koaBody from 'koa-body';
const multer = require('#koa/multer');
const upload = multer();
const fs = require('fs');
/**
* setup router for authenticated API routes
*/
const apiRouter = new Router<DefaultState, Context>();
apiRouter.use(authenticateJWT);
apiRouter.get(
'/authenticated',
async (ctx) => (ctx.body = 'Hi, authenticated user!'),
);
/**
* upload file with koa/multer
*/
apiRouter.post('/file-upload', upload.any('file'), async (ctx) => {
console.log('ctx.request.body', ctx.request.body);
console.log('ctx.request.files', ctx.request.files); // displays properties in the server logs, but I can not set properties to ctx.request.files.filename, for example
const type = ctx.request.type;
const user = ctx.state.user;
try {
const fileName = ctx.request.body.filename;
const fileContents = ctx.request.body.path; // The S3 function below tells me this path doesn't exist, even though I see it in the logs and it is accurate
const fileType = 'image/jpeg';
console.log(`name: ${fileName}`);
console.log(`contents: ${fileContents}`);
console.log(`type: ${fileType}`);
try {
const fileId = addUploadedFile(fileName, fileContents, fileType, user);
if (fileId) {
ctx.status = 200;
ctx.body = {
status: 'success',
};
} else ctx.status = 400;
} catch {
ctx.status = 400;
}
} catch(err) {
console.log(`error ${err.message}`)
}
});
const app = new Koa();
app.use(apiRouter.routes()).use(apiRouter.allowedMethods());
export const apiApp = mount('/api/1', app);
I have tried countless iterations of the code above. A primary issue is that ctx.request.files will return data when the POST executes, but in writing the code an error of the object being "potentially undefined" pops up when I try to set variables to properties such as ctx.request.files.testimage, .file, etc...
A version the came close to success:
apiRouter.post('/file-upload', upload.single('file'), async (ctx) => {
console.log('ctx.file', ctx.file);
const type = ctx.request.type;
const user = ctx.state.user;
console.log('user: ', JSON.stringify(user, null, 2));
try {
const fileName = ctx.file.fieldname;
const fileContents = fs.createReadStream(new Uint8Array(ctx.file.buffer)); // The S3 function does not like this buffer, neither as-is or when explicitly converted to Uint8Array as shown here
const fileType = ctx.file.mimetype;
try {
const fileId = addUploadedFile(fileName, fileContents, fileType, user);
if (fileId) {
ctx.status = 200;
ctx.body = {
status: 'success',
};
} else ctx.status = 400;
} catch {
ctx.status = 400;
}
} catch(err) {
console.log(`error ${err.message}`)
}
});
Using multer's Single function worked well for accessing properties of ctx.file (as seen above), but the path was not exposed as one of the properties (hence my moving on to use multer's Any function), so I tried to used ctx.file.buffer with the S3 function to no avail.
S3 Upload:
import AWS from 'aws-sdk';
import { db, User } from 'server/db';
const fs = require('fs');
const spacesEndpoint = new AWS.Endpoint('nyc3.digitaloceanspaces.com');
const s3 = new AWS.S3({
endpoint: spacesEndpoint,
accessKeyId: process.env.FILE_UPLOAD_SPACES_KEY,
secretAccessKey: process.env.FILE_UPLOAD_SPACES_SECRET,
});
const spacesFileUploadBucket = 'myta-uploads';
export async function addUploadedFile(
fileName: string,
fileContents: string,
fileType: string,
user: User,
): Promise<string | null | undefined> {
const fileNameRegex = /^[a-zA-Z0-9-._]{1,30}$/;
if (!fileName.match(fileNameRegex)) {
throw new Error('Invalid file name');
}
const body = fs.createReadStream(fileContents);
const params = {
Bucket: spacesFileUploadBucket,
Key: fileName,
Body: body,
Type: fileType,
ACL: 'private',
};
if (!user) throw new Error('No user given');
let fileId;
await s3.putObject(params, async function (err) {
if (err) throw new Error('Could not add file to storage');
fileId = await addFileUploadedToDb(fileName, user);
});
return fileId;
}
async function addFileUploadedToDb(fileName: string, user: User) {
const file = await db.fileUpload.create({
data: {
fileName: fileName,
uploader: { connect: { id: user.id } },
},
});
return file.id;
}

Upload to Amazon S3 from public URL

I would like to use the AWS iOS SDK to upload an image directly from a public url to a S3 bucket.
My goal is to avoid downloading then uploading the image, which would be obviously slower.
I tried to naively pass the URL to AWSS3TransferUtility.uploadFile like so :
import AWSCore
import AWSS3
// ...
AWSS3TransferUtility.register(
with: AWSServiceManager.default().defaultServiceConfiguration!,
transferUtilityConfiguration: AWSS3TransferUtilityConfiguration(),
forKey: "foo"
)
let utility = AWSS3TransferUtility.s3TransferUtility(forKey: "foo")!
let imageURL = URL(string: "https://via.placeholder.com/150/")!
utility
.uploadFile(
imageURL,
bucket: "<bucket name>",
key: "bar.jpeg",
contentType: "image/jpg",
expression: nil,
completionHandler: nil)
.continueWith {
if let error = $0.error {
print("Error: \(error.localizedDescription)")
}
return nil
}
But it seems to accept only url of local files and return the error
The operation couldn’t be completed. (com.amazonaws.AWSS3TransferUtilityErrorDomain error 4.)
Any idea if this is possible and how ?
Based on #jarmod idead I was able to do it using a Lambda (simplified here) :
const https = require('https');
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
function loadImage(url) {
return new Promise((resolve, reject) => {
https.get(url, (resp) => {
if(resp.statusCode > 200) {
reject('The image could not be loaded (HTTP ' + resp.statusCode + ')');
}
resp.setEncoding('binary');
let chunks = [];
resp.on('data', (chunk) => {
chunks.push(Buffer.from(chunk, 'binary'));
});
resp.on('end', () => {
let binary = Buffer.concat(chunks);
resolve(binary);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
reject(err);
});
});
}
exports.handler = async (event, context, callback) => {
try {
const url = ...;
const bucket = ...;
const key = ...;
const buffer = await loadImage(url);
const destparams = {
Bucket: bucket,
Key: key,
Body: buffer,
ContentType: "image"
};
await s3.putObject(destparams).promise();
return "OK";
} catch(error) {
return error;
}
};
Calling a Lambda using AWSLambda is quite simple :
let lambda = AWSLambdaInvocationRequest()!
lambda.functionName = // Lambda name
lambda.invocationType = .requestResponse
let parameters: [String: Any] = [:] // Parameters to pass to the lambda
let payload = try! JSONSerialization.data(withJSONObject: parameters, options: [])
lambda.payload = payload
AWSLambda.default().invoke(lambda) { response, error in
//...
}

How to convert JSON to CSV as attachment via email Ionic 3

I am implementing an offline feature for my clients that would allow them to submit forms offline and re-submit later(mainly for no service). I want to allow a fail safe for them to email the data to us just in case the submission gets stuck. I cam using the Ionic Email Composer plugin and everything works fine if I want to send The JSON data, however I would prefer if my users were to just send a CSV file. I am unsure on what is the best option to convert my JSON code to CSV and then send as an attachment.
.ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
import { Storage } from '#ionic/storage';
import { EmailComposer } from '#ionic-native/email-composer';
import { Angular5Csv } from 'angular5-csv/Angular5-csv';//Testing
import { ReapService } from '../../services/reap-service';
#IonicPage()
#Component({
selector: 'page-offline-data',
templateUrl: 'offline-data.html',
})
export class OfflineDataPage {
private offlineData:any [] =[];
private isData:boolean = false;
private emailJSONData:any = [];
private test:any;
constructor(public navCtrl: NavController,
public navParams: NavParams,
public storage: Storage,
public viewCtrl: ViewController,
private emailComposer: EmailComposer,
public reap: ReapService) {
}
ionViewDidLoad(){
this.storage.get('offlineSubmission').then((data)=>{
this.offlineData=data;
console.log(data);
/************ TESTING *********************/
console.log(JSON.stringify(data));
for(let i=0;i<data.length;i++){
this.emailJSONData.push(data[i]["Info"]);
}
console.log(this.emailJSONData);
this.test = new Angular5Csv(this.emailJSONData,'Form data');
console.log(this.test.csv);
});
if(this.offlineData==[]){
this.isData = false;
console.log(this.isData);
}
else{
this.isData = true;
}
}
dismiss() {
this.viewCtrl.dismiss();
}
sendEmail(){
this.emailComposer.isAvailable().then((available: boolean) =>{
if(available) {
//Now we know we can send
}
},(err) => {
this.reap.presentToast('Error Emailing Data');
console.log(err);
});
let email = {
to: 'john#doe.com',
cc: 'jane#doe.comm',
//bcc: ['john#doe.com', 'jane#doe.com'],
attachments: [
'file://'+this.test+'.csv'//Where I want to include the csv
],
subject: 'Offline Data Submissions',
body: this.emailJSONData.toString(),//Currently sending the raw JSON in body
isHtml: false
};
// Send a text message using default options
this.emailComposer.open(email);
}
}
JSON test code: I only need the Info part of the Array.
[{"Type":"SIG","Info":[{"personnel":{"ID":"1","FullName":"Test User","default":"false"}},{"signature":"data:image/png;base64,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"}],"Status":"Pending"}]
I am currently testing with Angular5csv, but I'm not sure If I can use Ionic File or any other useful plugins that may help get what I need.
You can download the csv file into IOS device file system using cordova-plugin-file. You can attach the same file path using attachment property in Email Composer plugin.
So I finally found my answer, which in my case would not be ideal to create a csv from the complex objects I will be using, but an example here:
convertToCSV(offlineData) {
var csv: any = ''
var line: any = ''
var offlineLength = offlineData.length
console.log(offlineLength);
//Header
for (var i = 0; i < offlineLength; i++) {
var formType = offlineData[i]['Type'];
line += formType+ '\r\n\r\n';
var SpT = offlineData[i]['Info'].length
for (var k = 0; k < SpT; k++){
line += Object.keys(offlineData[i]['Info'][k]);
if (line != '') line += ';'
}
line += '\r\n\r\n';
}
csv += line + '\r\n';
//offlineData
for (let i = 0; i < offlineLength; i++) {
line = ''
for (var j = 0; j < SpT; j++) {
if (line != '') line += ';'
switch(offlineData[i]['GPS'][j]){
case offlineData[i]['GPS'][j] == 'ID':
line += offlineData[i]['GPS'][j]['ID'];
break;
case 'Lat':
line += 'Lat';
break;
case ['Lon']:
line += 'Lon';
break;
default:
line += 'Test';
break;
}
}
csv += line + '\r\n'
}
return csv
}
saveAsCsv() {
var csv: any = this.convertToCSV(this.csvData)
console.log(csv)
var fileName: any = "test.csv"
this.File.writeFile(this.File.dataDirectory, fileName, csv ,{ replace: true })
.then(() => {
let email = {
to: 'email#email',
attachments: [
this.file.dataDirectory + 'test.csv'
],
subject: 'subject',
body: 'body text...',
isHtml: true
};
this.emailComposer.open(email);
})
.catch((err) => {
console.error(err);
});
}
Of course this isn't the exact data I was using in my question, but the convertToCSV function would allow you to manipulate your object and then spit it out to csv via email.
Credit for my answer is here.

How to send mail with attachments in Flutter?

I found dart plugin called mailer3: "^1.1.9". Previously I create an image in mobile temp directory. In Flutter mobile app I try to send this saved picture using mailer3 plugin as a mail. The mail reach the destination, I don't get error but seems lost the attachment in process.
In dart it works very well and send the attachment as well. In flutter I can use the temp directory to show the image in app but cannot be able attache to mail.
The image location is in the temp folder of the device:
'/data/user/0/com.myApp.myApp/app_flutter/20180700087.jpg'
I can show the image using below code:
new FileImage(File('$newDekontImage'),
Error:
E/flutter (21184): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter (21184): FileSystemException: Cannot open file, path = '/data/user/0/com.myApp.myApp/app_flutter/20180700087.jpg' (OS Error: No such file or directory, errno = 2)
How to send mail with attachments in Flutter with provided information in this question?
The Flutter Code:
// TODO: SEND MAIL
void _sendMail() async {
if (!_formKey.currentState.validate()) {
return;
} else {
_formKey.currentState.save();
var _options = new GmailSmtpOptions()
..username = “mymailaddress#gmail.com"
..password = “myPassword”;
var _emailTransport = new SmtpTransport(_options);
var _envelope = new Envelope()
..from = "mymailaddress#gmail.com"
..recipients.add(_receiverMailAddress)
..subject = "${_userDekontDetails[0][0].toString()} - Receipt”
..attachments.add(await new Attachment(file: await new File('$newDekontImage')))
..text = "${_userDekontDetails[0][0].toString()} - Receipt"
..html = '<h3>${_userDekontDetails[0][0].toString()} Receipt.</h3>'
'<p>Hi, registered under my name, I am sending the receipt (${widget._currentUserReceiptNo}) with attached to this mail.</p>'
'<p></p>'
'<h5>Regards, </br></h5>'
'${_userDekontDetails[0][0].toString()}';
_emailTransport.send(_envelope)
..then((envelope) => print('Email sent'))
..catchError((e) => print('Error occured: $e'));
}
}
As of this writing, the mailer3 plugin is outdated and mailer is the most up-to-date plugin for sending emails. The mailer plugin currently contains important fixes that mailer2 and mailer3 has. I suggest opting to use mailer package instead of mailer3.
Here's a port of your code snippet from mailer3 to mailer
_sendMail(String username, String accessToken) async {
// Read https://pub.dev/documentation/mailer/latest/smtp_server_gmail/gmailSaslXoauth2.html
var _emailTransport = gmailSaslXoauth2(username, accessToken);
var _envelope = new Message()
..from = "mymailaddress#gmail.com"
..recipients.add("recepient#gmail.com")
..subject = '{EMAIL_SUBJECT_GOES_HERE}'
// Read https://pub.dev/documentation/mailer/latest/mailer/FileAttachment-class.html
..attachments
.add(FileAttachment(File('{FILE_PATH}')))
..text = '{PLAIN_TEXT_GOES_HERE}'
..html = '{HTML_CONTENT_GOES_HERE}';
send(_envelope, _emailTransport)
..then((envelope) => print('Email sent'))
..catchError((e) => print('Error occured: $e'));
}
I have used enough_mail, Hope it will be helpful.
MessageBuilder messageBuilder = MessageBuilder();
Future<bool> onFileSelect(BuildContext context) async {
final result = await FilePicker.platform
.pickFiles(type: FileType.any, allowMultiple: true, withData: true);
if (result == null) {
return false;
}
for (final file in result.files) {
final lastDotIndex = file.path.lastIndexOf('.');
MediaType mediaType;
if (lastDotIndex == -1 || lastDotIndex == file.path.length - 1) {
mediaType = MediaType.fromSubtype(MediaSubtype.applicationOctetStream);
} else {
final ext = file.path.substring(lastDotIndex + 1);
mediaType = MediaType.guessFromFileExtension(ext);
}
messageBuilder.addBinary(file.bytes, mediaType, filename: file.name);
}
return true;
}
Future<void> sendMail(BuildContext buildContext) async {
setState(() {
needToFreezeUi = true;
});
MySnackBar.show(buildContext, MySnackBar.loadingIcon, "Please wait...!");
SmtpClient smtpClient = SmtpClient(domain, isLogEnabled: true);
try {
await smtpClient.connectToServer(
"$serverPrefix.${userInfo.domainName}",
smtpServerPort,
isSecure: isSmtpServerSecure
);
await smtpClient.ehlo();
await smtpClient.authenticate(userInfo.email, userInfo.password);
messageBuilder.from = [MailAddress('', userInfo.email)];
messageBuilder.to = [MailAddress('', toTextEditCtl.text)];
messageBuilder.cc = selectedCCEmailInfos.map((e) => MailAddress('',e.emailAddress)).toList();
messageBuilder.bcc = selectedBCCEmailInfos.map((e) => MailAddress('',e.emailAddress)).toList();
messageBuilder.subject = subjectTextEditCtl.text;
String htmlText = await htmlEditorController.getText();
messageBuilder.addTextHtml(htmlText);
messageBuilder.hasAttachments ? messageBuilder.getPart(
MediaSubtype.multipartAlternative,
recursive: false
) : messageBuilder.addPart(
mediaSubtype: MediaSubtype.multipartAlternative,
insert: true
);
if (!messageBuilder.hasAttachments) {
messageBuilder.setContentType(
MediaType.fromSubtype(MediaSubtype.multipartAlternative)
);
}
MimeMessage mimeMessage = messageBuilder.buildMimeMessage();
SmtpResponse smtpResponse = await smtpClient.sendMessage(mimeMessage);
MySnackBar.hide(buildContext);
if(smtpResponse.isOkStatus){
MySnackBar.show(buildContext,MySnackBar.successIcon,"Mail send successfully");
clearInputFields(buildContext);
}else {
MySnackBar.show(buildContext,MySnackBar.errorIcon,"Something went wrong, please try again!");
}
} on SmtpException catch (e) {
MySnackBar.show(buildContext,MySnackBar.errorIcon,"Something went wrong, please try again!");
}
setState(() {
needToFreezeUi = false;
});
}
============================
dependencies:
enough_mail: ^1.3.4
html_editor_enhanced: ^1.4.0
file_picker: ^3.0.2+2

Send a recorded file via Filetransfer with Cordova/Phonegap

I am trying to send a voice recording that I recorded via the Media plugin.
When I try to send the file I get this FileError.NOT_FOUND_ERR error:
Error opening file /myRecording100.wav: Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0xa358640 {NSFilePath=/myRecording100.wav, NSUnderlyingError=0xa34fb30 "The operation couldn’t be completed. No such file or directory"}
2014-08-06 17:02:26.919 Bring Me[40961:c07] FileTransferError {
code = 1;
source = "/myRecording100.wav";
target = "http://XXXX.xom";
}
However, I can play the voice recording after recording it.
Why would I be able to play the file (showing that the file was recorded and saved correctly) but FileTransfer be unable to send it?
Here is my code (for ios):
var my_recorder = null;
var mediaFileFullName = null; // iOS
var mediaRecFile = "myRecording100.wav";
var checkFileOnly = false;
/******
Call when start recording
******/
function startRecording() {
checkFileOnly = false;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onSuccessFileSystem, function() {
console.log("***test: failed in creating media file in requestFileSystem");
});
}
function onSuccessFileSystem(fileSystem) {
if (checkFileOnly === true) {
// Get File and send
fileSystem.root.getFile(mediaRecFile, { create: false, exclusive: false }, onOK_GetFile, onFail_GetFile);
}
else {
// Create File
fileSystem.root.getFile(mediaRecFile, { create: true, exclusive: false }, onOK_SaveFile, onFail_GetFile);
}
}
/* Save the file*/
function onOK_SaveFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
my_recorder = new Media(mediaFileFullName,
function() { document.location ="address_form.html"; // Redirect the user to an other page },
function(err) { console.log("playAudio():callback Record Error: "+err);}
);
my_recorder.startRecord();
}
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
/*
// Read the recorded file is WORKING !
my_player = new Media(mediaFileFullName, onMediaCallSuccess, onMediaCallError);
my_player.play();
*/
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(mediaFileFullName, "https://SERVER_ADDRESS", win, fail, options);
}
/******
Called when stop recording
******/
function stopRecording() {
if (my_recorder) {
my_recorder.stopRecord();
}
}
Since the v1.0 of File plugin, to upload a file in the filesystem via the file-transfer plugin, you'll need to use the .toURL() method to access to it.
If you are upgrading to a new (1.0.0 or newer) version of File, and
you have previously been using entry.fullPath as arguments to
download() or upload(), then you will need to change your code to use
filesystem URLs instead.
FileEntry.toURL() and DirectoryEntry.toURL() return a filesystem URL
of the form
So the correct code is :
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(fileEntry.toURL(), "https://SERVER_ADDRESS", win, fail, options);
}
I got the exact same issue on iOS,and FileUploadOptions didn't work for me.
In case someone is struggling as well, the solution for me has been to switch to LocalFileSystem.Temporary.
Here there is a snippet which shows a full example (not tested on Android):
var accessType = LocalFileSystem.TEMPORARY; // It was LocalFileSystem.PERSISTENT;
/** Utility function to return a fileEntry together with the metadata. */
var getFile = function(name, create, successCallback, failCallback) {
WL.Logger.debug("Request for file " + name + " received, create is " + create + ".");
var onSuccessFileSystem = function(fileSystem) {
fileSystem.root.getFile(name, { create: create, exclusive: false },
function(fileEntry){
WL.Logger.debug("Success, file entry for " + name + " is " + JSON.stringify(fileEntry));
fileEntry.getMetadata(function(metadata){
WL.Logger.debug("File entry " + name + " metadata is: " + JSON.stringify(metadata));
successCallback(fileEntry, metadata);
}, function(err) {
WL.Logger.debug("Fail to retrieve metadata, error: " + JSON.stringify(err));
if(failCallback) failCallback(err);
});
},
function(err) {
WL.Logger.error("Failed to retrieve the media file " + name + ".");
if(failCallback) failCallback(err);
});
}
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(accessType, 0, onSuccessFileSystem, function(err) {
WL.Logger.error("Failed to access file system.");
if(failCallback) failCallback(err);
});
};
var Recorder = declare([ ], {
mediaSrc : null,
mediaObj : null,
constructor : function(data, domNode){
this.mediaSrc = "new_recording.wav";
},
startRecord : function() {
var self = this;
var startRecording = function(source) {
var onMediaCallSuccess = function() { WL.Logger.debug("Media object success."); };
var onMediaCallError = function(err) { WL.Logger.error("Error on the media object: " + JSON.stringify(err)); };
self.mediaObj = new Media(source, onMediaCallSuccess, onMediaCallError);
self.mediaObj.startRecord();
};
// On iOS, first I need to create the file and then I can record.
if (deviceCheck.phone.ios) {
WL.Logger.debug("iOS detected, making sure the file exists.");
getFile(this.mediaSrc, true, function(fileEntry){ startRecording(fileEntry.fullPath); });
} else {
if (!deviceCheck.phone.android)
WL.Logger.warn("Don't know the device, trying to record ...");
else
WL.Logger.debug("Android detected.");
startRecording(this.mediaSrc);
}
},
stopRecord : function() {
this.mediaObj.stopRecord();
this.mediaObj.release();
},
play: function() {
var p,
playSuccess = function() { WL.Logger.debug("Play success."); p.release(); },
playFail = function() { WL.Logger.debug("Play fail."); };
p = new Media(this.mediaSrc, playSuccess, playFail);
p.play();
},
getData : function(successCallback, failCallback) {
var fileName = (deviceCheck.phone.android ? "/sdcard/" : "") + this.mediaSrc;
WL.Logger.debug("Asking for the file entry ... ");
getFile(this.mediaSrc, false,
function(fileEntry, metadata) {
WL.Logger.debug("Success: I found a file entry: " + fileEntry.nativeURL + ", size is " + metadata.size);
fileEntry.file(function(file) {
WL.Logger.debug("Success: file retrieved!");
var reader = new FileReader();
reader.onloadend = function(evt) {
WL.Logger.debug("Sending content and event data to success callback.");
successCallback(this.result, metadata, evt);
};
reader.readAsDataURL(file);
}, function(err){
WL.Logger.error("Error: Impossible to retrieve the file");
failCallback(err);
})
}, function(err){
WL.Logger.error("Fail: no file entry found: " + JSON.stringify(err));
failCallback(err);
});
}
});
There is a bit of Worklight (debug output) and dojo (declare), but this code could be useful as reference.

Resources