Is there a way to get a callback from - meteor

I am downloading files to the client using Iron Router.
Router.route('zipfile', {
where: 'server',
path: '/zipfile/:name/:targetName',
action: function() {
var name = this.params.name;
var targetName = this.params.targetName;
var filename = `${ZIP_DIR}/${name}`;
var file = fs.readFileSync(filename);
var headers = {
'Content-type': 'application/zip',
'Content-disposition' : `attachment; filename=${targetName}.zip`,
};
this.response.writeHead(200, headers);
return this.response.end(file);
}
});
I wanted to know when the download has completed so I can then delete the source file on the server. Is there an easy way of doing that?

You could use the onAfterAction hook
Router.onAfterAction(function(req, res, next) {
// in here next() is equivalent to this.next();
}, {
only: ['zipfile'],
where: 'server
});

Related

ostrio:files with Dropbox on Meteor

I'm trying to setup ostrio:files to work with dropbox. Can someone please explain me how to do it?
I've registered on dropbox and generated token using ostrio:files manual but I don't know how to upload files to dropbox.
After upload, when I check local Images db there are all images information but I don't know how to send it to Dropbox.
After file select, it says “upload successful” but there are no files uploaded on Dropbox.
dropbox.js
import { Meteor } from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';
var Dropbox,
Request,
bound,
client,
fs,
Images = {};
if (Meteor.isServer) {
Dropbox = require("dropbox").Dropbox;
const fetch = require("node-fetch");
bound = Meteor.bindEnvironment(function (callback) {
return callback();
});
client = new Dropbox({
accessToken: "mytoken", // Use your token here
fetch: fetch,
});
}
Request = require("request");
fs = require("fs");
Images.files = new FilesCollection({
debug: false, // Change to `true` for debugging
storagePath: "assets/app/uploads/uploadedFiles",
collectionName: "uploadedFiles",
allowClientCode: false,
onAfterUpload: function (fileRef) {
// In onAfterUpload callback we will move file to DropBox
try {
var self = this;
var makeUrl = function (path, fileRef, version) {
client
.sharingCreateSharedLink({ path: path, short_url: false })
.then(function (response) {
bound(function () {
const url = response.url.replace("dl=0", "raw=1");
var upd = {
$set: {},
};
upd["$set"]["versions." + version + ".meta.pipeFrom"] = url;
upd["$set"]["versions." + version + ".meta.pipePath"] = path;
self.collection.update(
{
_id: fileRef._id,
},
upd,
function (error) {
if (error) {
return console.error(error);
}
// Unlink original files from FS after successful upload to DropBox
self.unlink(self.collection.findOne(fileRef._id), version);
}
);
});
})
.catch(function (error) {
console.error(error);
});
};
var writeToDB = function (fileRef, version, data) {
// DropBox already uses random URLs
// No need to use random file names
client
.filesUpload({
path: "/" + fileRef._id + "-" + version + "." + fileRef.extension,
contents: data,
autorename: false,
})
.then(function (response) {
bound(function () {
// The file was successfully uploaded, generating a downloadable link
makeUrl(response.path_display, fileRef, version);
});
})
.catch(function (error) {
bound(function () {
console.error(error);
});
});
};
var readFile = function (fileRef, vRef, version) {
fs.readFile(vRef.path, function (error, data) {
bound(function () {
if (error) {
return console.error(error);
}
writeToDB(fileRef, version, data);
});
});
};
var sendToStorage = function (fileRef) {
_.each(fileRef.versions, function (vRef, version) {
readFile(fileRef, vRef, version);
});
};
sendToStorage(fileRef);
} catch (error) {
// There was an error while uploading the file to Dropbox, displaying the concerned file
console.log(
"The following error occurred while removing " + fileRef.path
);
// Removing the file from the file system
fs.unlink(fileRef.path, function (error) {
if (error) {
console.error(error);
}
});
// Removing the file from the collection
Images.files.remove(
{
_id: fileRef._id,
},
function (error) {
if (error) {
console.error(error);
}
}
);
}
},
onBeforeRemove: function (cursor) {
return false;
},
interceptDownload: function (http, fileRef, version) {
// Files are stored in Dropbox, intercepting the download to serve the file from Dropbox
var path, ref, ref1, ref2;
path =
(ref = fileRef.versions) != null
? (ref1 = ref[version]) != null
? (ref2 = ref1.meta) != null
? ref2.pipeFrom
: void 0
: void 0
: void 0;
if (path) {
// If file is moved to DropBox
// We will pipe request to DropBox
// So, original link will stay always secure
Request({
url: path,
headers: _.pick(
http.request.headers,
"range",
"accept-language",
"accept",
"cache-control",
"pragma",
"connection",
"upgrade-insecure-requests",
"user-agent"
),
})
.on("response", function (response) {
if (response.statusCode == 200) {
response.headers = _.pick(
response.headers,
"accept-ranges",
"cache-control",
"connection",
"content-disposition",
"content-length",
"content-type",
"date",
"etag"
);
response.headers["Cache-control"] =
"only-if-cached, public, max-age=2592000";
}
})
.pipe(http.response);
return true;
} else {
// While file is not yet uploaded to DropBox
// We will serve file from FS
return false;
}
},
});
// if (Meteor.isServer) {
// // Intercept File's collection remove method to remove file from DropBox
// var _origRemove = Images.files.remove; // Catching the original remove method to call it after
// Images.files.remove = function (search) {
// var cursor = this.collection.find(search);
// cursor.forEach(function (fileRef) {
// _.each(fileRef.versions, function (vRef) {
// var ref;
// if (
// vRef != null
// ? (ref = vRef.meta) != null
// ? ref.pipePath
// : void 0
// : void 0
// ) {
// client
// .filesDeleteV2({ path: vRef.meta.pipePath })
// .catch(function (error) {
// bound(function () {
// console.error(error);
// });
// });
// }
// });
// });
// // Call original method
// _origRemove.call(this, search);
// };
// }
export default Images; // import in other files
uploadForm.js
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import Images from '/lib/dropbox.js';
Template.uploadForm.onCreated(function () {
this.currentUpload = new ReactiveVar(false);
});
Template.uploadForm.helpers({
currentUpload() {
return Template.instance().currentUpload.get();
}
});
Template.uploadForm.events({
'change #fileInput'(e, template) {
console.log("file opened");
if (e.currentTarget.files && e.currentTarget.files[0]) {
// We upload only one file, in case
// multiple files were selected
const upload = Images.files.insert({
file: e.currentTarget.files[0],
streams: 'dynamic',
chunkSize: 'dynamic'
}, false);
upload.on('start', function () {
template.currentUpload.set(this);
console.log("start upload");
});
upload.on('end', function (error, fileObj) {
if (error) {
alert(`Error during upload: ${error}`);
} else {
alert(`File "${fileObj.name}" successfully uploaded`);
}
template.currentUpload.set(false);
});
upload.start();
}
}
});
uploadForm.html
<template name="uploadForm">
{{#with currentUpload}}
Uploading <b>{{file.name}}</b>:
<span id="progress">{{progress.get}}%</span>
{{else}}
<input id="fileInput" type="file" />
{{/with}}
</template>
when i try to upload I get this in server console
[FilesCollection] [File Start Method] img.png - ms75Ah76svrFEGh2f
[FilesCollection] [Upload] [DDP Start Method] Got #-1/1 chunks, dst: img.png
[FilesCollection] [Upload] [DDP] Got #1/1 chunks, dst: img.png
[FilesCollection] [Upload] [DDP] Got #-1/1 chunks, dst: img.png
[FilesCollection] [Upload] [finish(ing)Upload] -> tmp\ms75Ah76svrFEGh2f.png
[FilesCollection] [Upload] [finish(ed)Upload] -> tmp\ms75Ah76svrFEGh2f.png
The following error occurred while removing tmp\ms75Ah76svrFEGh2f.png
[FilesCollection] [remove({"_id":"ms75Ah76svrFEGh2f"})]
[FilesCollection] [unlink(ms75Ah76svrFEGh2f, undefined)]
[FilesCollection] [_preCollectionCursor.observe] [changed]: ms75Ah76svrFEGh2f
[FilesCollection] [_preCollectionCursor.observe] [removed]: ms75Ah76svrFEGh2f

Make sync method in Meteor js

I tried to make a sync method according to https://themeteorchef.com/tutorials/synchronous-methods
Here's my code:
Server side
Meteor.methods({
sendDataToMixer: function(){
var Future = require('fibers/future');
var SSH = require('simple-ssh');
var future = new Future();
var ssh = new SSH({
host: 'host',
user: 'user',
pass: 'pass'
});
ssh
.exec('mkdir check133', {
out: console.log.bind(console)
})
.start({
success: function(){
future.return("success!");
console.log(future);
},
error: function(err){
future.return(err);
}
});
return future.wait();
}
});
Client side:
Meteor.call('sendDataToMixer', function(err, data){
if(err){
console.log('Ooops from future');
}
else{
console.log(data);
console.log('clicked');
}
});
But this solution doesn't work as a sync one. What am I doing wrong here?

How do you do client side routing when saving via methods?

I want to save some data and show it in a view template. So I want to do like the example below but using methods.
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
url: $(e.target).find('[name=url]').val(),
title: $(e.target).find('[name=title]').val()
};
post._id = Posts.insert(post);
Router.go('postPage', post);
}
});
I tried this:
'insertClubData': function(clubname, capacity, description, homepage){
var currentUserId = Meteor.userId();
var club = {
clubname: clubname,
description: description,
capacity: parseInt(capacity),
homepage: homepage,
createdAt: new Date(),
visitors: 0,
occupancy: 0,
trend: "club-1",
createdBy: currentUserId
}
club._id = clubs.insert(club);
Router.go('club', club);
},
but I get the error:
Exception while invoking method 'insertClubData' TypeError: Object
function router(req, res, next) { I20160425-14:04:55.724(2)? //XXX
this assumes no other routers on the parent stack which we should
probably fix
I understand that this is because Router.go is a client side method. But I also understand that you should avoid server side routing. So what's the most elegant solution?
This is my route:
Router.route('/club/:_id', {
name: 'club',
template: 'club',
data: function(){
return clubs.findOne({_id: this.params._id})
}
});
How about, you call the method from the client and in the callback on success you do the routing. For example:
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
url: $(e.target).find('[name=url]').val(),
title: $(e.target).find('[name=title]').val()
};
Meteor.call('insertPost', post, function(error, id) {
if (error) {
alert(error)
} else {
Router.go('postPage', {_id: id});
}
});
}
});
and on the server
Meteor.methods({
insertPost: function(post) {
// do checks
id = Posts.insert(post);
return id;
}
});
Does that work for you?

Server side route to download file

I've got a server side route I'm using to download a file. This is called from a client side button click and everything is working fine. However, once the button has been clicked once it will not work again until another route is loaded and you go back. How can I code it so that the button can be clicked multiple times and the server side route be fired each time?
My button code looks like this...
'click #view_document_download': function (event, tmpl) {
Router.go('/download_document/' + this._id);
}
And my server side route looks like this...
Router.route('/download_document/:_id', function () {
//Get the file record to download
var file = files.findOne({_id: this.params._id});
//Function to take a cfs file and return a base64 string
var getBase64Data = function(file2, callback) {
var readStream = file2.createReadStream();
var buffer = [];
readStream.on('data', function(chunk) {
buffer.push(chunk);
});
readStream.on('error', function(err) {
callback(err, null);
});
readStream.on('end', function() {
callback(null, buffer.concat()[0].toString('base64'));
});
};
//Wrap it to make it sync
var getBase64DataSync = Meteor.wrapAsync(getBase64Data);
//Get the base64 string
var base64str = getBase64DataSync(file);
//Get the buffer from the string
var buffer = new Buffer(base64str, 'base64');
//Create the headers
var headers = {
'Content-type': file.original.type,
'Content-Disposition': 'attachment; filename=' + file.original.name
};
this.response.writeHead(200, headers);
this.response.end(buffer, 'binary');
}, { where: 'server' });
use a element instead of js 'click' event
page html
page js in server
Router.route("/download_document/:fileId", function(){
var file = files.findOne({_id: this.params.fileId});
var contentFile = //file text
let headers = {
'Content-Type': 'text/plain',
'Content-Disposition': "attachment; filename=file.txt"
};
this.response.writeHead(200, headers);
this.response.end(contentFile);
},
{where: "server", name: "download"}
);
Maybe you should just return an Object from your Server via a method and form it to a file on the client side? if possible..
To create a file on the client side is really simple, and you don't have to deal with Routers at this point.
function outputFile(filename, data) {
var blob = new Blob([data], {type: 'text/plain'}); // !note file type..
if(window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
}
else{
var elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem)
elem.click();
document.body.removeChild(elem);
}
}
function getContentAndOutputFile() {
var content = document.getElementById('content').value;
outputFile('file.txt', content);
}
<input id="content" value="test content"/>
<button onClick="getContentAndOutputFile()">Create File</button>

How to set a timeout on a http.request() in Node?

I'm trying to set a timeout on an HTTP client that uses http.request with no luck. So far what I did is this:
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
}
/* This does not work TOO MUCH... sometimes the socket is not ready (undefined) expecially on rapid sequences of requests */
req.socket.setTimeout(myTimeout);
req.socket.on('timeout', function() {
req.abort();
});
req.write('something');
req.end();
Any hints?
2019 Update
There are various ways to handle this more elegantly now. Please see some other answers on this thread. Tech moves fast so answers can often become out of date fairly quickly. My answer will still work but it's worth looking at alternatives as well.
2012 Answer
Using your code, the issue is that you haven't waited for a socket to be assigned to the request before attempting to set stuff on the socket object. It's all async so:
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
});
req.on('socket', function (socket) {
socket.setTimeout(myTimeout);
socket.on('timeout', function() {
req.abort();
});
});
req.on('error', function(err) {
if (err.code === "ECONNRESET") {
console.log("Timeout occurs");
//specific error treatment
}
//other error treatment
});
req.write('something');
req.end();
The 'socket' event is fired when the request is assigned a socket object.
Just to clarify the answer above:
Now it is possible to use timeout option and the corresponding request event:
// set the desired timeout in options
const options = {
//...
timeout: 3000,
};
// create a request
const request = http.request(options, response => {
// your callback here
});
// use its "timeout" event to abort the request
request.on('timeout', () => {
request.destroy();
});
See the docs:
At this moment there is a method to do this directly on the request object:
request.setTimeout(timeout, function() {
request.abort();
});
This is a shortcut method that binds to the socket event and then creates the timeout.
Reference: Node.js v0.8.8 Manual & Documentation
The Rob Evans anwser works correctly for me but when I use request.abort(), it occurs to throw a socket hang up error which stays unhandled.
I had to add an error handler for the request object :
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
}
req.on('socket', function (socket) {
socket.setTimeout(myTimeout);
socket.on('timeout', function() {
req.abort();
});
}
req.on('error', function(err) {
if (err.code === "ECONNRESET") {
console.log("Timeout occurs");
//specific error treatment
}
//other error treatment
});
req.write('something');
req.end();
There is simpler method.
Instead of using setTimeout or working with socket directly,
We can use 'timeout' in the 'options' in client uses
Below is code of both server and client, in 3 parts.
Module and options part:
'use strict';
// Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js
const assert = require('assert');
const http = require('http');
const options = {
host: '127.0.0.1', // server uses this
port: 3000, // server uses this
method: 'GET', // client uses this
path: '/', // client uses this
timeout: 2000 // client uses this, timesout in 2 seconds if server does not respond in time
};
Server part:
function startServer() {
console.log('startServer');
const server = http.createServer();
server
.listen(options.port, options.host, function () {
console.log('Server listening on http://' + options.host + ':' + options.port);
console.log('');
// server is listening now
// so, let's start the client
startClient();
});
}
Client part:
function startClient() {
console.log('startClient');
const req = http.request(options);
req.on('close', function () {
console.log("got closed!");
});
req.on('timeout', function () {
console.log("timeout! " + (options.timeout / 1000) + " seconds expired");
// Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js#L27
req.destroy();
});
req.on('error', function (e) {
// Source: https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js#L248
if (req.connection.destroyed) {
console.log("got error, req.destroy() was called!");
return;
}
console.log("got error! ", e);
});
// Finish sending the request
req.end();
}
startServer();
If you put all the above 3 parts in one file, "a.js", and then run:
node a.js
then, output will be:
startServer
Server listening on http://127.0.0.1:3000
startClient
timeout! 2 seconds expired
got closed!
got error, req.destroy() was called!
Hope that helps.
For me - here is a less confusing way of doing the socket.setTimeout
var request=require('https').get(
url
,function(response){
var r='';
response.on('data',function(chunk){
r+=chunk;
});
response.on('end',function(){
console.dir(r); //end up here if everything is good!
});
}).on('error',function(e){
console.dir(e.message); //end up here if the result returns an error
});
request.on('error',function(e){
console.dir(e); //end up here if a timeout
});
request.on('socket',function(socket){
socket.setTimeout(1000,function(){
request.abort(); //causes error event ↑
});
});
Elaborating on the answer #douwe here is where you would put a timeout on a http request.
// TYPICAL REQUEST
var req = https.get(http_options, function (res) {
var data = '';
res.on('data', function (chunk) { data += chunk; });
res.on('end', function () {
if (res.statusCode === 200) { /* do stuff with your data */}
else { /* Do other codes */}
});
});
req.on('error', function (err) { /* More serious connection problems. */ });
// TIMEOUT PART
req.setTimeout(1000, function() {
console.log("Server connection timeout (after 1 second)");
req.abort();
});
this.abort() is also fine.
You should pass the reference to request like below
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
});
req.setTimeout(60000, function(){
this.abort();
});
req.write('something');
req.end();
Request error event will get triggered
req.on("error", function(e){
console.log("Request Error : "+JSON.stringify(e));
});
Curious, what happens if you use straight net.sockets instead? Here's some sample code I put together for testing purposes:
var net = require('net');
function HttpRequest(host, port, path, method) {
return {
headers: [],
port: 80,
path: "/",
method: "GET",
socket: null,
_setDefaultHeaders: function() {
this.headers.push(this.method + " " + this.path + " HTTP/1.1");
this.headers.push("Host: " + this.host);
},
SetHeaders: function(headers) {
for (var i = 0; i < headers.length; i++) {
this.headers.push(headers[i]);
}
},
WriteHeaders: function() {
if(this.socket) {
this.socket.write(this.headers.join("\r\n"));
this.socket.write("\r\n\r\n"); // to signal headers are complete
}
},
MakeRequest: function(data) {
if(data) {
this.socket.write(data);
}
this.socket.end();
},
SetupRequest: function() {
this.host = host;
if(path) {
this.path = path;
}
if(port) {
this.port = port;
}
if(method) {
this.method = method;
}
this._setDefaultHeaders();
this.socket = net.createConnection(this.port, this.host);
}
}
};
var request = HttpRequest("www.somesite.com");
request.SetupRequest();
request.socket.setTimeout(30000, function(){
console.error("Connection timed out.");
});
request.socket.on("data", function(data) {
console.log(data.toString('utf8'));
});
request.WriteHeaders();
request.MakeRequest();

Resources