How to give the client the result of the upload with Meteor? [duplicate] - file-upload

This question already has answers here:
Meteor: Calling an asynchronous function inside a Meteor.method and returning the result
(4 answers)
Closed 3 years ago.
I'm not sure if this question has ever been asked before has I've been browsing internet looking for answers about file upload and I can't find the right one.
Here is the thing : I want to upload a file to the server. And this is OK. I've found a large number of discussion talking about how you can upload a file to the server.
Now my problem is I can't find a way to tell the client that the file was effectively uploaded or if there was an error...
I've been trying with
Meteor.call('methodName', args, function(error, result){//some code}); on the client and this on the server :
Meteor.methods({
saveFile: function (blob, name, path, encoding) {
var fs = __meteor_bootstrap__.require('fs');
var path = badgesHelper.cleanPath(path);
var name = badgesHelper.cleanName(name || 'file');
var encoding = encoding || 'binary';
path = Meteor.chroot + path + '/';
console.log(path, name, encoding);
var uploadResult = {success: false};
fs.writeFile(path + name, blob, encoding, function (error) {
if (error) {
uploadResult.error = error
Meteor.Error(500, 'An error occurred');
} else {
var date = new Date();
uploadResult.success = true;
}
});
return uploadResult;
}
});
And I just can't find a way to send the uploadResult map to the client. Since the call is asynchronous, node hits the "return" before the callback function is call and that uploadResult has the real result of the function...
I don't want to use filepicker.io I need to know how to do this without any package.
If I'm doing this the wrong way, please advise, as for now I'm stuck there...
Thank you

The map is not required here since you are only returning a boolean value, you can return it directly. In case of error, you can just throw it instead of false. Also, you must throw new Meteor.Error() instead of just Meteor.Error().
The code with the changes:
Meteor.methods({
saveFile: function (blob, name, path, encoding) {
var fs = __meteor_bootstrap__.require('fs');
var path = badgesHelper.cleanPath(path);
var name = badgesHelper.cleanName(name || 'file');
var encoding = encoding || 'binary';
path = Meteor.chroot + path + '/';
console.log(path, name, encoding);
var uploadResult = false;
fs.writeFile(path + name, blob, encoding, function (error) {
if (error) {
// EDIT: Throws an error
// error.error = 500; error.reason = "An error occurred";
throw new Meteor.Error(500, 'An error occurred');
} else {
// result = true
return true;
}
});
}
});

Related

Write rows to BigQuery via nodejs BigQuery Storage Write API

It seems quite new, but just hoping someone here has been able to use nodejs to write directly to BigQuery storage using #google-cloud/bigquery-storage.
There is an explanation of how the overall backend API works and how to write a collection of rows atomically using BigQuery Write API but no such documentation for nodejs yet. A recent release 2.7.0 documents the addition of said feature but there is no documentation, and the code is not easily understood.
There is an open issue requesting an example but thought I'd try my luck to see if anyone has been able to use this API yet.
Suppose you have a BigQuery table called student with three columns id,name and age. Following steps will get you to load data into the table with nodejs storage write api.
Define student.proto file as follows
syntax = "proto2";
message Student {
required int64 id = 1;
optional string name = 2;
optional int64 age = 3;
}
Run the following at the command prompt
protoc --js_out=import_style=commonjs,binary:. student.proto
It should generate student_pb.js file in the current directory.
Write the following js code in the current directory and run it
const {BigQueryWriteClient} = require('#google-cloud/bigquery-storage').v1;
const st = require('./student_pb.js')
const type = require('#google-cloud/bigquery-storage').protos.google.protobuf.FieldDescriptorProto.Type
const mode = require('#google-cloud/bigquery-storage').protos.google.cloud.bigquery.storage.v1.WriteStream.Type
const storageClient = new BigQueryWriteClient();
const parent = `projects/${project}/datasets/${dataset}/tables/student`
var writeStream = {type: mode.PENDING}
var student = new st.Student()
var protoDescriptor = {}
protoDescriptor.name = 'student'
protoDescriptor.field = [{'name':'id','number':1,'type':type.TYPE_INT64},{'name':'name','number':2,'type':type.TYPE_STRING},{'name':'age','number':3,'type':type.TYPE_INT64}]
async function run() {
try {
var request = {
parent,
writeStream
}
var response = await storageClient.createWriteStream(request);
writeStream = response[0].name
var serializedRows = []
//Row 1
student.setId(1)
student.setName('st1')
student.setAge(15)
serializedRows.push(student.serializeBinary())
//Row 2
student.setId(2)
student.setName('st2')
student.setAge(15)
serializedRows.push(student.serializeBinary())
var protoRows = {
serializedRows
}
var proto_data = {
writerSchema: {protoDescriptor},
rows: protoRows
}
// Construct request
request = {
writeStream,
protoRows: proto_data
};
// Insert rows
const stream = await storageClient.appendRows();
stream.on('data', response => {
console.log(response);
});
stream.on('error', err => {
throw err;
});
stream.on('end', async () => {
/* API call completed */
try {
var response = await storageClient.finalizeWriteStream({name: writeStream})
response = await storageClient.batchCommitWriteStreams({parent,writeStreams: [writeStream]})
}
catch(err) {
console.log(err)
}
});
stream.write(request);
stream.end();
}
catch(err) {
console.log(err)
}
}
run();
Make sure your environment variables are set correctly to point to the file containing google cloud credentials.
Change project and dataset values accordingly.

Load XMLHttpRequest from facebook Instant game returns empty result, even though I have just saved the data before getting it from server

The response from server returns empty result despite just saving data earlier in that contextID with success. Most time it returns the json data but sometimes in between it returns empty string leading to createNewGameAsync() function instead of going directly to populateFromBackend() function. I am creating backend from https://glitch.com/edit/#!/panoramic-tendency project on glitch.
loadData: function () {
var contextID = FBInstant.context.getID();
console.log('loadData from ' + contextID);
FBInstant.player.getSignedPlayerInfoAsync(contextID)
.then(function (signedPlayerInfo) {
var url = 'https://panoramic-tendency.glitch.me' + '/get-match'
var sig = signedPlayerInfo.getSignature();
var method = 'POST'
var payload = { 'signature': sig };
return req(url, method, payload);
})
.then(function (result) {
if (result.empty) {
return this.createNewGameAsync();
} else {
return Promise.resolve(result.data);
}
}.bind(this)).then(function (backendData){
this.populateFromBackend(backendData);
}.bind(this))
.catch(function (error) {
this.displayError(error);
}.bind(this));
Solved. I was saving the FbInstant.Player.getPhoto() url as well in database. During encoding with getSignedPlayerInfoAsync() the signature generated was not valid format, and server couldn't decode it resulting in null value.

Using API tags for a library

I'm currently creating a library for an API. The endpoints have optional tags, and so I'm trying to create a way to use them in the functions.
import * as request from "request";
class Api {
key: string;
constructor(userInput: string) {
this.key = userInput;
}
champions(tags: object) {
Object.keys(tags).forEach(function(key) {
console.log(key + " = " + tags[key])
})
request(`https://api.champion.gg/v2/champions?api_key=${this.key}&${tags}`, function (error, response, body) {
if(!error && response.statusCode == 200) {
let info = JSON.parse(body)
}
});
}
}
var test = new Api("key")
test.champions({"champData": ["kda", "damage"], "rawr": ["xd", "lmao"]})
So far, the combining of Object.keys and forEach has allowed me to get the response of champData=kda,damage and rawr=xd,lmao, however, I need to be able to assign these to a variable that's usable in the URL. How can I get this to work?
Another issue that may occur later on is that, between each tag, there needs to be an & symbol, but not at the end. I apologize for throwing multiple problems into one, but because this is my first experience with something like this, I'm having many issues.
You can use Object.entries() and URLSearchParams()
const tags = {a:1, b:2, c:3};
const params = new URLSearchParams();
const key = "def";
Object.entries(tags).forEach(([key, prop]) => params.set(key, prop));
const url = `https://api.champion.gg/v2/champions?api_key=${key}&${params.toString()}`;
console.log(url);

How to manage depending functions in nodejs

I am trying to teach myself nodejs and expressjs, however coming from java and c++ this is proving difficult to get used to.
I made a simple and messy module that it is supposed to return a weather forecast for a given zip code.
The way this happens is by taking the user zip code and using a google api to generate the geo coordinates for that zip code. I get the coordinates from the JASON file and then provide them to the next api call, this call is done to the forecast.io api and this time the weather data for the location is also taken from a JASON file.
Coming from java and with a not so solid background on JavaScript I am having a hard time making these two functions wait for one another, in this case I need the google api call to finish first because the coordinates it will provide are needed for the second api call. Can someone take a look at this code and tell me if the strategy I used is correct/ provide a suggestion so that I can know what is done in javascript in situations like this.
here is the code:
// The required modules.
var http = require("http");
var https = require("https");
//result object
var resultSet = {
latitude :"",
longitude:"",
localInfo:"",
weather:"",
humidity:"",
pressure:"",
time:""
};
//print out error messages
function printError(error){
console.error(error.message);
}
//Forecast API required information:
//key for the forecast IO app
var forecast_IO_Key = "this is my key, not publishing for security reasons";
var forecast_IO_Web_Adress = "https://api.forecast.io/forecast/";
//Create Forecast request string function
function createForecastRequest(latitude, longitude){
var request = forecast_IO_Web_Adress + forecast_IO_Key + "/"
+ latitude +"," + longitude;
return request;
}
//Google GEO API required information:
//Create Google Geo Request
var google_GEO_Web_Adress = "https://maps.googleapis.com/maps/api/geocode/json?address=";
function createGoogleGeoMapRequest(zipCode){
var request = google_GEO_Web_Adress+zipCode + "&sensor=false";
return request;
}
function get(zipCode){
// 1- Need to request google for geo locations using a given zip
var googleRequest = https.get(createGoogleGeoMapRequest(zipCode), function(response){
//console.log(createGoogleGeoMapRequest(zipCode));
var body = "";
var status = response.statusCode;
//a- Read the data.
response.on("data", function(chunk){
body+=chunk;
});
//b- Parse the data.
response.on("end", function(){
if(status === 200){
try{
var coordinates = JSON.parse(body);
resultSet.latitude = coordinates.results[0].geometry.location.lat;
resultSet.longitude = coordinates.results[0].geometry.location.lng;
resultSet.localInfo = coordinates.results[0].address_components[0].long_name + ", " +
coordinates.results[0].address_components[1].long_name + ", " +
coordinates.results[0].address_components[2].long_name + ", " +
coordinates.results[0].address_components[3].long_name + ". ";
}catch(error){
printError(error.message);
}finally{
connectToForecastIO(resultSet.latitude,resultSet.longitude);
}
}else{
printError({message: "Error with GEO API"+http.STATUS_CODES[response.statusCode]})
}
});
});
function connectToForecastIO(latitude,longitude){
var forecastRequest = https.get(createForecastRequest(latitude,longitude),function(response){
// console.log(createForecastRequest(latitude,longitude));
var body = "";
var status = response.statusCode;
//read the data
response.on("data", function(chunk){
body+=chunk;
});
//parse the data
response.on("end", function(){
try{
var weatherReport = JSON.parse(body);
resultSet.weather = weatherReport.currently.summary;
resultSet.humidity = weatherReport.currently.humidity;
resultSet.temperature = weatherReport.currently.temperature;
resultSet.pressure = weatherReport.currently.pressure;
resultSet.time = weatherReport.currently.time;
}catch(error){
printError(error.message);
}finally{
return resultSet;
}
});
});
}
}
//define the name of the outer module.
module.exports.get = get;
is the return statement properly placed? Is my use of finally proper in here? Please notice that I come from a java background and in java is perfectly fine to use the try{} catch(){} and finally{} blocks to execute closure code, it was the only way i managed this module to work. But now that i have incorporated some Express and I try to execute this module's method from another module, all I am getting is an undefined return.
You could use the Promise API, kind of like Futures in Java, so basically what you could do is wrap both functions in promises and the you could wait for resolve to execute the next function
var googleRequest = function(zipcode) {
return new Promise(function(resolve, reject) {
var request = https.get(createGoogleGeoMapRequest(zipCode), function(response) {
if (response.statusCode !== 200) {
reject(new Error('Failed to get request status:' + response.statusCode));
}
var body = "";
//a- Read the data.
response.on("data", function(chunk) {
body+=chunk;
});
//b- Parse the data.
response.on("end", function(body) {
var coordinates = JSON.parse(body);
resultSet.latitude = coordinates.results[0].geometry.location.lat;
resultSet.longitude = coordinates.results[0].geometry.location.lng;
resultSet.localInfo = coordinates.results[0].address_components[0].long_name + ", " +
coordinates.results[0].address_components[1].long_name + ", " +
coordinates.results[0].address_components[2].long_name + ", " +
coordinates.results[0].address_components[3].long_name + ". ";
resolve(resultSet);
})
});
request.on('error', function(err) {
reject(err);
});
});
}
After that you could just do
googleRequest(90210).then(function(result) {
connectToForecastIO(result.latitude, result.longitude);
}
You can find out more about Promise's usage in the Promise API docs
You should also note that there are several libraries available that allow for promise based http requests such as fetch

Example code to use GridFS using mongoskin to upload file from form

I am using mongoskin to connect mongodb in my project. Now I have requirement to use GridFs to upload images, audio etc. I have one HTML form to upload these files.
I tried to find out example code to upload file using mongoskin however could't find any good one.
Please help.
After spending many hours; I am able to use mongoskin to upload file to Gridfs. Not sure if this is perfect code however sharing it here because I couldn't find any single working code on searching Google :-)
https://github.com/dilipkumar2k6/gridfs-mongoskin
var DBModule = require('./DBModule.js');
var Grid = require('gridfs-stream');
var mongoskin = require('mongoskin');
//Upload file to server and also update the database
exports.uploadContent = function (req, res) {
console.log('Calling uploadFile inside FileUploadService');
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
console.log('uploadFile after busboy fieldname: ' + fieldname + ", file : " + file + ", filename : " + filename);
// make sure the db instance is open before passing into `Grid`
var gfs = Grid(DBModule.db, mongoskin);
//Get metadata var host = req.headers['host'];
var metadata = {contentType: mimetype};
var writestream = gfs.createWriteStream({filename: filename, metadata: metadata});
file.pipe(writestream);
writestream.on('close', function (file) {
// return URL to acces the uploaded content
var path = "contents/" + file._id;
res.json({"path": path});
});
writestream.on('error', function (err) {
log.error({err: err}, 'Failed to upload file to database');
res.status(constants.HTTP_CODE_INTERNAL_SERVER_ERROR);
res.json({error: err});
});
});
};
//view file from database
exports.previewContent = function (req, res) {
var contentId = new DBModule.BSON.ObjectID(req.params.contentid);
console.log('Calling previewFile inside FileUploadService for content id ' + contentId);
var gs = DBModule.db.gridStore(contentId, 'r');
gs.read(function (err, data) {
if (!err) {
//res.setHeader('Content-Type', metadata.contentType);
res.end(data);
} else {
log.error({err: err}, 'Failed to read the content for id ' + contentId);
res.status(constants.HTTP_CODE_INTERNAL_SERVER_ERROR);
res.json({error: err});
}
});
};
Try this to store the data using gridfs (by default uses mongoskin). It worked for me.
var ObjectID = require('mongodb').ObjectID,
GridStore = require('mongodb').GridStore;
exports.saveMedia = function(db, media, next) {
console.log(media)
db.open(function (err, db) {
// Create a file and open it
var gridStore = new GridStore(db, new ObjectID(), "w");
gridStore.open(function (err, gridStore) {
// Write some content to the file
gridStore.write(new Buffer(media), function (err, gridStore) {
// Flush the file to db
gridStore.close(function (err, fileData)
//returns filename
next(null, fileData)
});
});
});
});
}