如何发送一个variables从服务器到客户端meteor?
我有一个文本input和一个button的页面。 当我将链接插入YouTubevideo到文本字段,然后按下button – video下载到本地文件夹。
问题:我怎么能发送链接到本地副本的下载video回客户端?
更一般的问题:我如何发送一个variables从服务器到客户端(这个variables是临时的,不会被存储在任何地方)?
我现在的代码是:
客户端代码
if (Meteor.isClient) { Path = new Meteor.Collection("path"); Meteor.subscribe("path"); Template.hello.events( { 'submit .form' : function() { var link = document.getElementById("youtube-url").value; Meteor.call('download', link); event.preventDefault(); } } ); }
服务器代码('收集'部分不工作)
if (Meteor.isServer) { Meteor.startup(function () { Meteor.methods({ download: function (link) { var youtubedl = Npm.require('youtube-dl'); var Fiber = Npm.require("fibers"); var dl = youtubedl.download(link, './videos'); // called when youtube-dl finishes dl.on('end', function(data) { console.log('\nDownload finished!'); Fiber(function() { Path = new Meteor.Collection("path"); Path.insert({path: './videos/' + data.filename}); }) }); } }); }); }
谢谢!
问题的答案分为两部分:(a)处理Meteor方法中的asynchronous函数,(b)使用youtube-dl
包。
Meteor方法中的asynchronous函数
基本上有2种方法可以在Meteor的方法中使用asynchronous函数:使用future
和wrapAsync
。 如果你看看meteor的来源,你会看到使用future
wrapAsync
本身: https : //github.com/meteor/meteor/blob/master/packages/meteor/helpers.js#L90 。 您也可以直接使用fibers
,但不推荐使用 。
以下是如何使用它们的通用示例:
'use strict'; if (Meteor.isClient) { Template.methods.events({ 'click #btnAsync' : function() { console.log('Meteor.call(asyncMethod)'); Meteor.call('asyncMethod', 1000, function(error, result) { if (error) { console.log('Meteor.call(asyncMethod): error:', error); } else { console.log('Meteor.call(asyncMethod): result:', result); } }); }, 'click #btnFuture' : function() { console.log('Meteor.call(futureMethod)'); Meteor.call('futureMethod', 1000, function(error, result) { if (error) { console.log('Meteor.call(futureMethod): error:', error); } else { console.log('Meteor.call(futureMethod): result:', result); } }); }, 'click #btnFiber' : function() { console.log('Meteor.call(fiberMethod)'); Meteor.call('fiberMethod', 1000, function(error, result) { if (error) { console.log('Meteor.call(fiberMethod): error:', error); } else { console.log('Meteor.call(fiberMethod): result:', result); } }); } }); } if (Meteor.isServer) { var demoFunction = function(duration, callback) { console.log('asyncDemoFunction: enter.'); setTimeout(function() { console.log('asyncDemoFunction: finish.'); callback(null, { result: 'this is result' }); }, duration); console.log('asyncDemoFunction: exit.'); }; var asyncDemoFunction = Meteor.wrapAsync(demoFunction); var futureDemoFunction = function(duration) { var Future = Npm.require('fibers/future'); var future = new Future(); demoFunction(duration, function(error, result) { if (error) { future.throw(error); } else { future.return(result); } }); return future.wait(); }; var fiberDemoFunction = function(duration) { var Fiber = Npm.require('fibers'); var fiber = Fiber.current; demoFunction(duration, function(error, result) { if (error) { fiber.throwInto(new Meteor.Error(error)); } else { fiber.run(result); } }); return Fiber.yield(); }; Meteor.methods({ asyncMethod: function (duration) { return asyncDemoFunction(duration); }, futureMethod: function (duration) { return futureDemoFunction(duration); }, fiberMethod: function (duration) { return fiberDemoFunction(duration); } }); }
你可能也想看看Meteor.bindEnvironment()
和future.resolver()
更复杂的情况。
Christian Fritz为wrapAsync
使用提供了正确的模式,然而,从最初的问题开始的两年内, youtube-dl
软件包的API已经发生了变化。
使用youtube-dl
软件包
由于API的更改,如果您运行其代码,则服务器会在其控制台中引发可见的exception:
Exception while invoking method 'download' TypeError: Object function (videoUrl, args, options) { ... } has no method 'download'
meteor返回给客户端undefined
值:
here is the path: undefined
下面的代码正在工作(只需用您的pathreplacedownloadDir)并返回文件名到客户端:
here is the path: test.mp4
文件index.html
<head> <title>meteor-methods</title> </head> <body> {{> hello}} </body> <template name="hello"> <form> <input type="text" id="youtube-url" value="https://www.youtube.com/watch?v=alIq_wG9FNk"> <input type="button" id="downloadBtn" value="Download by click"> <input type="submit" value="Download by submit"> </form> </template>
文件index.js
:
'use strict'; if (Meteor.isClient) { //Path = new Meteor.Collection("path"); //Meteor.subscribe("path"); Template.hello.events( { 'submit .form, click #downloadBtn' : function() { var link = document.getElementById("youtube-url").value; //Meteor.call('download', link); Meteor.call('download', link, function(err, path) { if (err) { console.log('Error:', err); } else { console.log("here is the path:", path); } }); event.preventDefault(); } } ); } if (Meteor.isServer) { var fs = Npm.require('fs'); var youtubedl = Npm.require('youtube-dl'); var downloadSync = Meteor.wrapAsync(function(link, callback) { var fname = 'test.mp4'; // by default it will be downloaded to // <project-root>/.meteor/local/build/programs/server/ var downloadDir = './'; console.log('\nStarting download...'); // var dl = youtubedl.download(link, './videos'); var dl = youtubedl(link, [], []); dl.on('info', function(info) { console.log('\nDownload started: ' + info._filename); }); // dl.on('end', function(data) { dl.on('end', function() { console.log('\nDownload finished!'); //callback(null, './videos/' + data.filename); callback(null, fname); }); dl.on('error', function(error) { console.log('\nDownload error:', error); callback(new Meteor.Error(error.message) ); }); dl.pipe(fs.createWriteStream(downloadDir + fname)); }); Meteor.methods({ download: function (link) { return downloadSync(link); } }); }
当前的API不允许在保存文件时获取youtube的文件名。 如果你想用youtube的文件名保存文件(如第一个问题所提供的),你需要使用youtube-dl
软件包的getInfo()
方法。
你可以使用这个小包装: https : //atmosphere.meteor.com/package/client-call 。 它允许从服务器调用客户端方法,方式与Meteor.methods
相反。
我觉得如果你刚从方法调用中返回你想要的path,会更容易。 所有你需要做的就是让YouTube下载同步 – 这就是meteor的做事方式。
这应该工作:
if (Meteor.isServer) { var youtubedl = Npm.require('youtube-dl'); var sync = Meteor.wrapAsync(function(url, callback) { var dl = youtubedl.download(link, './videos'); dl.on('end', function(data) { console.log('\nDownload finished!'); callback(null, './videos/' + data.filename); }); }); Meteor.methods({ download: function (link) { return sync(link); } }); }
然后,在客户端上,使用:
Meteor.call('download', link, function(err, path) { console.log("here is the path:", path); });
您必须在方法定义中使用asynchronousFuture,如本答案所述 。 然后,只有在asynchronous下载操作完成之后,您才能够等待回拨到客户端