I'm using nativescript-imagepicker plugin to select images from phone gallery. One of the things this plugin allows me to get, is the path to the file.
I need to be able to upload this selected file to a server, using form data. For that i need to create a file object first.
How can i use a file path, to create a file object?
For uploading images from the photo gallery I would highly suggest using Nativescsript background http. To upload the images to the server you will have to save them within the app so that they can be uploaded. I followed the example shown here Upload example.
Once you have saved the images locally if you want additional data you will need to use multipartUpload and construct a request that would look something like this.
let BackgroundHTTP = require('nativescript-background-http')
let session = BackgroundHTTP.session('some unique session id')
let request: {
url: 'your.url.to/upload/images',
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
}
description: 'Uploading local images to the server'
}
//photos should have at least the filename from when you saved it locally.
let params = []
photos.forEach(photo => {
params.push({name: photo.name, filename: photo.filename, value: 'ANY STRING DATA YOU NEED'})
}
let task = session.multipartUpload(params, request)
task.on('progress', evt => {
console.log('upload progress: ' + ((evt.currentBytes / evt.totalBytes) * 100).toFixed(1) + '%')
}
task.on('error', evt => {
console.log('upload error')
console.log(evt)
}
task.on('complete', evt => {
//this does not mean the server had a positive response
//but the images hit the server.
// use evt.responseCode to determine the status of request
console.log('upload complete, status: ' + evt.responseCode)
}
Related
I'm trying to upload an image to an http server that supposedly accepts files in "the standard way", whatever that means. I've combined a bunch of examples from the Internet, each of which does a tiny part of what I want, into this solution.
'srcBitmap' is a byteArray containing the JPG data.
val response: HttpResponse = httpClient.submitFormWithBinaryData(
url = URLUploadFile,
formData = formData {
append("bitmapName", "image.jpg")
append("image", srcBitmap, Headers.build {
append(HttpHeaders.ContentType, "image/jpg")
append(HttpHeaders.ContentDisposition, "filename=image.jpg")
})
},
block = {
headers {
append(HttpHeaders.ContentType, contentTypeString)
append(HttpHeaders.CacheControl, "no-cache")
append("my-app-authtoken", PREFKEY_AUTHTOKEN)
append("my-app-id", PREFKEY_USERID)
}
contentType(ContentType.Application.Json)
body = jsonBody.toString()
})
The main "body" part is some json that gets passed in the 'block' parameter. This data is arriving safely as intended.
But the binary data of the image itself is either not showing up on the server side, or is being ignored by the server because I don't have some "key" value set appropriately.
Is this the correct way to upload a file using Ktor? And if not, what am I doing wrong?
The second append call is a correct way of sending a part with the name image and the filename image.jpg. The problem is that you can't send both application/json and multipart/form-data content in one request.
Actually yours is a correct way, I was facing the same problem with my back-end guy that he receives my request as a byteArray file and couldn't recognized. So what I did was specify the files directly to the body instead of using submitFormWithBinaryData, as below..
'srcBitmap' is a byteArray containing the JPG data.
httpClient.post<RESPONSE>(URL) {
headers {
append(HttpHeaders.Accept, ContentType.Application.Json)
}
body = MultiPartFormDataContent(
formData {
this.append(FormPart("bitmapName", "image.jpg"))
this.appendInput(
key = "image",
headers = Headers.build {
append(
HttpHeaders.ContentDisposition,
"filename=image.jpg"
)
},
) { buildPacket { writeFully(srcBitmap) } }
}
)
}
i'm trying to upload a large file (500+Mb, but could be even bigger) to our php server, using an app written in Ionic4+Angular+Cordova, on an emulator with Android 10.
I set up a system to upload the file in chunks.
It reads the file choosen by the user using THIS PLUGIN, chunk by chunk (5Mb per chunk).
Then it proceeds to send it to our server performing a POST request with Content-type multipart/form-data.
The file goes to server, server saves it, says "OK", then the app proceeds to send the following chunk.
Everything works fine, for the first 25/29 chunks.
Then, the POST request fails with
POST http://192.168.1.2/work/path/to/webservices/uploadChunks.php net::ERR_FILE_NOT_FOUND
I tried:
starting at another point in the file instead of byte 0 - got the same error
reading the file chunk by chunk, without making any POST request- could cycle the whole 500Mb file
reading the file chunk by chunk and making the POST requests, but not sending the chunks with them - could execute every single call without any error, through the end of the file
reading the file chunk by chunk and sending them to ANOTHER webservice - got the same error
reading the file chunk by chunk and performing a POST request to another webservice, with content-type application/json and putting the formData object into the request body (not sure this is a valid test tho) - could execute every single call without any error, through the end of the file
Checking out memory snapshots taken in chrome inspector during different chunks upload did not show any sign of memory leak.
The case was tested on a rather old device, where the same procedure caused the app to exit, without signaling any error (not even in logcat apparently).
Here is the piece of code used to chunk and send the file:
const generatedName = 'some_name_for_file';
// Path obtained from fileChooser plugin
let path_to_file = 'content://com.android.externalstorage.documents/document/primary%3ADownload%2Ffilename.avi'
const min_chunk_size = (5 * 1024 * 1024);
// Converting path to file:// path
this.filePath.resolveNativePath(path_to_file).then((resolvedPath) => {
return this.fileAPI.resolveLocalFilesystemUrl(resolvedPath);
}, (error) => {
console.log('ERROR FILEPATH');
console.log(error);
return Promise.reject('Can not access file.<br>Code : F - ' + error);
}).then(
(entry) => {
path_to_file = entry.toURL();
console.log(path_to_file);
(entry as FileEntry).file((file) => {
//Getting back to the zone
this.ngZone.run(() => {
// Re-computing chunk size to be sure we do not get more than 10k chunks (very remote case)
let file_chunk_size = file.size / 10000;
if (file_chunk_size < min_chunk_size) {
file_chunk_size = min_chunk_size;
}
//Total number of chunks
const tot_chunk = Math.ceil(file.size / file_chunk_size);
const reader = new FileReader();
let retry_count = 0; //Counter to check on retries
const readFile = (nr_part: number, part_start: number, length: number) => {
// Computing end of chunk
const part_end = Math.min(part_start + length, file.size);
// Slicing file to get desired chunk
const blob = file.slice(part_start, part_end);
reader.onload = (event: any) => {
if (event.target.readyState === FileReader.DONE) {
let formData = new FormData();
//Creating blob
let fileBlob = new Blob([reader.result], {
type: file.type
});
formData.append('file', fileBlob, generatedName || file.name);
formData.append('tot_chunk', tot_chunk.toString());
formData.append('nr_chunk', nr_part.toString());
// UPLOAD
const sub = this.http.post('http://192.168.1.2/path/to/webservice/uploadChunk.php', formData).subscribe({
next: (response: any) => {
console.log('UPLOAD completed');
console.log(response);
retry_count = 0;
if (response && response.status === 'OK') {
//Emptying form and blob to be sure memory is clean
formData = null;
fileBlob = null;
// Checking if this was the last chunk
if (part_end >= file.size) {
// END
callback({
status: 'OK'
});
} else {
// Go to next chunk
readFile(nr_part + 1, part_end, length);
}
//Clearing post call subscription
sub.unsubscribe();
} else {
//There was an error server-side
callback(response);
}
},
error: (err) => {
console.log('POST CALL ERROR');
console.log(err);
if (retry_count < 5) {
setTimeout(() => {
retry_count++;
console.log('RETRY (' + (retry_count + 1) + ')');
readFile(nr_part, part_start, length);
}, 1000);
} else {
console.log('STOP RETRYING');
callback({status:'ERROR'});
}
}
});
}
};
//If for some reason the start point is after the end point, we exit with success...
if (part_start < part_end) {
reader.readAsArrayBuffer(blob);
} else {
callback({
status: 'OK'
});
}
};
//Start reading chunks
readFile(1, 0, file_chunk_size);
});
}, (error) => {
console.log('DEBUG - ERROR 3 ');
console.log(error);
callback({
status: 'ERROR',
code: error.code,
message: 'Can not read file<br>(Code: 3-' + error.code + ')'
});
});
}, (error) => {
console.log('ERROR 3');
console.log(error);
return Promise.reject('Can not access file.<br>Code : 3 - ' + error);
}
);
I can not figure out what is going wrong. Can someone help me debug this, or knows what could be going on?
Thank you very much.
I still do not know what caused this issue, but i resolved using a PUT request instead of a POST request, sending the raw chunk, and putting additional data in custom headers (something like "X-nr-chunk" or "X-tot-chunk"). Upload completed fine without the error message.
I also used the cordova-advanced-http plugin, but i do not think it made a difference here, since it did not work with the POST request, like the other method (httpClient).
This has been tested on android only for now, not on iOS. I'll report if there is any problem. For now i consider this solved, but if you know what may have caused this problem, please share your thoughts.
Thanks everyone.
Background: I'm using Firebase Cloud Functions, the new Firestore Database, and storage bucket with an Android client.
What I want to accomplish:
When a user uploads a picture to a storage bucket, I want to use cloud functions to get the file path/link to the image location in the storage bucket and store this string as a new document under a new collection called "pictures" under the currently logged in user's document in Firestore.
That way, I can see the images each user has uploaded directly in Firestore and it makes it easier to pull a specific user's images down to the Android client.
What I've completed so far:
1. When a user logs in for the first time, it creates a user doc in the new Firestore Database.
2. A logged in user can upload an image to a storage bucket.
3. Using Firebase Cloud Functions, I managed to get the file path/link of the storage location as follows:
/**
* When a user selects a profile picture on their device during onboarding,
* the image is sent to Firebase Storage from a function running on their device.
* The cloud function below returns the file path of the newly uploaded image.
*/
exports.getImageStorageLocationWhenUploaded = functions.storage.object().onFinalize((object) => {
const filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
console.log(filePath);
});
Question: How do I get the currently logged in user and store this user's uploaded image file path/link as a new doc under this logged in user's documents within the Firestore database using Cloud Functions?
Currently, with Cloud Storage triggers, you don't have access to authenticated user information. To work around that, you'll have to do something like embed the uid in the path of the file, or add the uid as metadata in the file upload.
I had a similar problem where I wanted to associate a user uploaded image with his/her uid. I found an elegant solution that does not necessarily requires inserting the uid in the path of the file or even adding it as metadata in the file upload. In fact, the uid is securely transmitted to the database via the standard idToken encoding. This example employs a modified version of the generate-thumbnail cloud function example (found here) which I believe the author of the question was using/alluding to. Here are the steps:
Client side:
Create a trigger function that will run once the user uploaded the image - this functions will simply call the cloud function directly (via the httpsCallable method). The cloud function will receive the user uid (idToken encoded), along with any image metadata you may wish to send. The cloud function will then return a signed URL of the image.
const generateThumbnail = firebase.functions().httpsCallable('generateThumbnail');
const getImageUrl = (file) => {
firebase.auth().currentUser.getIdToken(true)
.then((idToken) => generateThumbnail({
idToken,
imgName: file.name,
contentType: file.type
}))
.then((data) => {
// Here you can save your image url to the app store, etc.
// An example of a store action:
// setImageUrl(data.data.url);
})
.catch((error) => {
console.log(error);
})
}
Create an image upload function – this is a standard file upload handling function; you can make the uid part of storage file path location for the image (if you want to) but you can also trigger a firebase function once the image has been uploaded. This is possible using the 3rd parameter of the on method. Include the trigger function above as the 3rd argument in here.
// Function triggered on file import status change from the <input /> tag
const createThumbnail = (e) => {
e.persist();
let file = e.target.files[0];
// If you are using a non default storage bucket use this
// let storage = firebase.app().storage('gs://your_non_default_storage_bucket');
// If you are using the default storage bucket use this
let storage = firebase.storage();
// You can add the uid in the image file path store location but this is optional
let storageRef = storage.ref(`${uid}/thumbnail/${file.name}`);
storageRef.put(file).on('state_changed', (snapshot) => {}, (error) => {
console.log('Something went wrong! ', error);
}, getImageUrl(file));
}
Server side:
Create a cloud function to convert the image into a resized thumbnail and generate a signed URL – this cloud function takes the image from storage, converts it into a thumbnail (basically reduces its dimensions but keeps initial aspect ratio) using ImageMagick (this is installed by default on all cloud function instances). It then generates a signed URL of the image location and returns it to the client side.
// Import your admin object with the initialized app credentials
const mkdirp = require('mkdirp');
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');
// Max height and width of the thumbnail in pixels.
const THUMB_MAX_HEIGHT = 25;
const THUMB_MAX_WIDTH = 125;
// Thumbnail prefix added to file names.
const THUMB_PREFIX = 'thumb_';
async function generateThumbnail(data) {
// Get the user uid from IdToken
const { idToken, imgName, contentType } = data;
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
const uid = decodedIdToken.uid;
// File and directory paths.
const filePath = `${uid}/thumbnail/${imgName}`;
const fileDir = path.dirname(filePath);
const fileName = path.basename(filePath);
const thumbFilePath = path.normalize(path.join(fileDir, `${THUMB_PREFIX}${fileName}`));
const tempLocalFile = path.join(os.tmpdir(), filePath);
const tempLocalDir = path.dirname(tempLocalFile);
const tempLocalThumbFile = path.join(os.tmpdir(), thumbFilePath);
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
return console.log('This is not an image.');
}
// Exit if the image is already a thumbnail.
if (fileName.startsWith(THUMB_PREFIX)) {
return console.log('Already a Thumbnail.');
}
// Cloud Storage files.
const bucket = initDb.storage().bucket('your_bucket_if_non_default');
const originalFile = bucket.file(filePath);
// Create the temp directory where the storage file will be downloaded.
// But first check to see if it does not already exists
if (!fs.existsSync(tempLocalDir)) await mkdirp(tempLocalDir);
// Download original image file from bucket.
await originalFile.download({ destination: tempLocalFile });
console.log('The file has been downloaded to', tempLocalFile);
// Delete the original image file as it is not needed any more
await originalFile.delete();
console.log('Delete the original file as it is not needed any more');
// Generate a thumbnail using ImageMagick.
await spawn('convert', [ tempLocalFile, '-thumbnail',
`${THUMB_MAX_WIDTH}x${THUMB_MAX_HEIGHT}>`, tempLocalThumbFile],
{ capture: ['stdout', 'stderr'] }
);
console.log('Thumbnail created at', tempLocalThumbFile);
// Uploading the Thumbnail.
const url = await uploadLocalFileToStorage(tempLocalThumbFile, thumbFilePath,
contentType);
console.log('Thumbnail uploaded to Storage at', thumbFilePath);
// Once the image has been uploaded delete the local files to free up disk space.
fs.unlinkSync(tempLocalFile);
fs.unlinkSync(tempLocalThumbFile);
// Delete the uid folder from temp/pdf folder
fs.rmdirSync(tempLocalDir);
await admin.database().ref(`users/${uid}/uploaded_images`).update({ logoUrl: url[0] });
return { url: url[0] };
}
// Upload local file to storage
exports.uploadLocalFileToStorage = async (tempFilePath, storageFilePath,
contentType, customBucket = false) => {
let bucket = initDb.storage().bucket();
if (customBucket) bucket = initDb.storage().bucket(customBucket);
const file = bucket.file(storageFilePath);
try {
// Check if file already exists; if it does delete it
const exists = await file.exists();
if (exists[0]) await file.delete();
// Upload local file to the bucket
await bucket.upload(tempFilePath, {
destination: storageFilePath,
metadata: { cacheControl: 'public, max-age=31536000', contentType }
});
const currentDate = new Date();
const timeStamp = currentDate.getTime();
const newDate = new Date(timeStamp + 600000);
const result = await file.getSignedUrl({
action: 'read',
expires: newDate
});
return result;
} catch (e) {
throw new Error("uploadLocalFileToStorage failed: " + e);
}
};
if (firebase.auth().currentUser !== null)
console.log("user id: " + firebase.auth().currentUser.uid);
Simple way to get userid of a user.
Iam new in ionic with sharepoint
I have developed a Mobile app using ionic3 with sharepoint.
Now i have to get user profile picture in my app.
I have tried these are the way can't achieve here is my tried code.
First way tried like this
Passing Url:-
"https://abc.sharepoint.com/sites/QA/_layouts/15/userphoto.aspx?size=M&accountname=admin#abc.onmicrosoft.com"
Second way tried like this
Passing Url:-
These url iam geting using people picker result. PictureURL property
"https://abc.sharepoint.com/User Photos/Profile Pictures/admin_abc_onmicrosoft_com_MThumb.jpg"
These Second method always return
401 UNAUTHORIZED
Above url using to call this method.
public downloadFile(url: string, fileName: string) {
let options = this._apiHeaderForImageURL();
this._http.get(url, options)
.subscribe((data) => {
//here converting a blob to base 64 For internal view purpose in image src
var reader = new FileReader();
reader.readAsDataURL(data.blob());
reader.onloadend = function () {
console.log("Base64", reader.result);
}
//Here Writing a blob file to storage
this.file.writeFile(this.file.externalRootDirectory, fileName, data.blob(), { replace: true })
.then((success) => {
console.log("File Writed Successfully", success);
}).catch((err) => {
console.log("Error While Wrinting File", err);
});
});
}
public _apiHeaderForImageURL() {
let headers = new Headers({ 'Content-Type': 'image/jpeg' });
headers.append('Authorization', 'Bearer ' + localStorage.getItem("token"));
let options = new RequestOptions({ headers: headers, responseType: 3 });
return options;
}
The first api call worked fine result also sucess but image not displayed properly. Thats the problem iam facing.
The result comes an default image like this only.
pls help me to achieve this. Any help warmly accepted.
Iam doning long time stuff to achieve this still i cant achieve pls give some idea.
Is any other way is available to get user picture in ionic 3 using sharepoint?
i have an error with the Image Upload from Facebook in my Titanium Software, everytime i want to upload an image from my App i get this:
Fail: REST API is deprecated for versions v2.1 and higher
But if i try the same code in the KitchenSink example app, it works perfect:
var xhr = Titanium.Network.createHTTPClient({
onload: function() {
// first, grab a "handle" to the file where you'll store the downloaded data
var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,'mygraphic.png');
f.write(this.responseData); // write to the file
var blob = f.read();
var data = {
caption: 'behold, a flower',
picture: blob
};
facebook.request('photos.upload', data, showRequestResult);
},
timeout: 10000
});
xhr.open('GET','http://www.pur-milch.de/files/www/motive/pm_motiv_kaese.jpg');
xhr.send();
And in my App:
function showRequestResult(e) {
var s = '';
if (e.success) {
s = "SUCCESS";
if (e.result) {
s += "; " + e.result;
}
} else {
s = "FAIL";
if (e.error) {
s += "; " + e.error;
}
}
alert(s);
}
Ti.App.hs_stats.addEventListener('touchend', function(e){
Ti.App.hs_stats.top = 255;
var xhr = Titanium.Network.createHTTPClient({
onload: function() {
// first, grab a "handle" to the file where you'll store the downloaded data
var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,'mygraphic.png');
f.write(this.responseData); // write to the file
var blob = f.read();
var data = {
caption: 'behold, a flower',
picture: blob
};
Ti.App.fb.request('photos.upload', data, showRequestResult);
},
timeout: 10000
});
xhr.open('GET','http://www.pur-milch.de/files/www/motive/pm_motiv_kaese.jpg');
xhr.send();
});
Looks like you're using the 'old' Facebook module for Appcelerator? I have image uploads working for Profiles and Pages (although Pages is a bit different, I'll explain later). Here's some quick code (I assume you already authenticated with Facebook):
var fb = require('facebook');
fb.appid = "xxxxxxxxxxxxxxxxx";
var acc = fb.getAccessToken();
fb.requestWithGraphPath('me/photos?access_token='+ acc, {picture:image, message: data}, "POST", showRequestResult);
The image variable is just a blob - It comes directly from event.media from a gallery selection or camera intent. data is the text for your status update.
In your tiapp.xml add these lines:
<property name="ti.facebook.appid">xxxxxxxxxxxxxxxxx</property>
and (if you're using Android and iOS - add both or just the platform you're using)
<modules>
<module platform="android">facebook</module>
<module platform="iphone">facebook</module>
</modules>
Now Pages were a bit strange:
var endPoint = 'https://graph.facebook.com/v2.1/' + pid + '/photos?access_token='+ acc;
xhr.open('POST',endPoint);
xhr.send({
message: data,
picture: image
});
You have to use an HTTP Request, as I couldn't get the requestWithGraphPath() to work with pages no matter what I tried.
pid is your page ID and you can get it, or a list of pages you are an admin for like so (again, create a new HTTP Request (xhr) and use this):
xhr.open("GET","https://graph.facebook.com/v2.1/me?fields=accounts{access_token,global_brand_page_name,id,picture}&access_token=" +fb.getAccessToken());
This will return the access token for each page, the global brand name (basically a clean version of the page name), it's id and the profile picture. The access token in this URL is YOUR personal access token (the &access_token= part).
As far as I can tell, these access tokens don't expire for pages, so you can save it in your app somewhere or if you REALLY want to be safe, you could grab a token before each post, but that's a bit much.
BONUS:
If you want to do video posts to pages:
var xhr = Titanium.Network.createHTTPClient();
var endPoint = 'https://graph-video.facebook.com/'+ pid +'/videos?access_token='+ acc;
xhr.open('POST',endPoint);
xhr.setRequestHeader("enctype", "multipart/form-data");
xhr.send({source:video, description:data});
and for profiles:
var acc = fb.getAccessToken();
var xhr = Titanium.Network.createHTTPClient();
var endPoint = 'https://graph-video.facebook.com/me/videos?access_token='+ acc;
xhr.open('POST',endPoint);
xhr.setRequestHeader("enctype", "multipart/form-data");
xhr.send({source:video, description:data});
video is another blob from either your camera or gallery event.media intent and data is the text you want to use for the status update.