【问题标题】:Wait for a file to finish copying before acting on it?等待文件完成复制后再对其进行操作?
【发布时间】:2014-01-08 15:05:09
【问题描述】:

我有一个简单的 node.js 应用程序,用于监视新 PDF 文件的目录。当它看到它们出现时,它会将它们 FTP 出来并将它们移动到另一个目录。

我遇到的问题是如果文件有点大(例如 10MB),我的应用程序在完成复制到目录之前就开始处理文件。

复制是通过网络进行的,因此也可能会减慢速度。我需要一种方法来告诉我的应用程序等到文件完成复制后再处理它。

最好的方法是什么?我已经尝试过“成长文件”模块,但它似乎不起作用,而且看起来开发人员已经放弃了它。

提前感谢您的帮助。

【问题讨论】:

  • 嗯。将文件复制到同一文件系统上的临时位置,然后将其移动到“待处理”目录中。该文件将在任何体面的操作系统上一次全部移动。
  • 传输完成后将文件发送为“xyz.inprogress”并重命名为“xyz.pdf”如何?
  • 是的,我确实让这变得比它需要的更复杂!我接受了@MarkSetchell 建议的想法,并让文件以“.inProgress”结尾,直到它完全转移。效果很好。

标签: node.js


【解决方案1】:

我遇到了几乎同样的问题,我需要在播放文件之前下载文件。我最终编写了这段代码,您可以轻松地为您的操作重写。

编辑:事实上,你几乎可以像使用代码一样,只是要小心下载的回调。

它利用回调来逐个下载每个文件(我遇到了带宽问题),但我有一个以前的版本,它开始所有下载,然后等待所有文件在回调之前都在磁盘上。

如果你想直接使用它,你需要一个名为 DOWNLOAD_DIR 的全局变量,其中包含下载目录的完整路径。

您还需要 http,但我认为您已经拥有它。

var http = require('http');

/*download
IN_: file_url
        string
        url of the file to download
     callback
COM: Download the specified file to DOWNLOAD_DIR/name_of_the_file, and callback the full path to the file
     callback null on error.
*/
function download(file_url, callback) {
    var options = {
        host: url.parse(file_url).host,
        port: 80,
        path: url.parse(file_url).pathname
    },
        file_name = url.parse(file_url).pathname.split('/').pop(),
    //Creating the file
        file = fs.createWriteStream(DOWNLOAD_DIR + file_name, {flags: 'w', encoding: 'binary'}),
    console.log('Downloading file from ' + file_url);
    console.log(LOG, '\tto ' + file_name);
    http.get(options, function (res) {
        res.pipe(file, {end: 'false'});
        //When the file is complete
        res.on('end', function () {
            //Closing the file
            file.end();
            console.log(LOG, '\t\tDownloaded '+ file_name);
            callback(DOWNLOAD_DIR + file_name);
        });
    });

    process.on('uncaughtException', function(err) {
        console.log('Can t download ' + file_url + '\t(' + err + ')');
        callback(null);
    });

}

/*download_all
IN_: list
        array of string
        Names of the files to download
     callback
COM: Download all the file one after another
*/
function download_all(list, callback) {
    var i = 0, 
        fe;

    function follow() {
        //If there is download to do
        if (i < list.length) {
            //Checking if the file already exist
            fe = fs.existsSync(DOWNLOAD_DIR + list[i].substr(list[i].lastIndexOf('/')));
            console.log('Checking ' + list[i]);
            if (!fe) {
                console.log('\tDo not exist');
                //If it doesn t, downloading it
                download(list[i], function () {
                    i = i + 1;
                    //And go to the next file
                    follow();
                });
            } else {
                //If it does, go to the next file
                console.log('\tExist');
                i = i + 1;
                follow();
            }
        } else {
            //When all files are downloaded
            console.log('end');
            callback();
        }
    }
    follow();
}

请注意,在生产代码中,您应该将 fs.existSync(下载中)替换为 fs.exist + 回调

编辑:这里是一次性下载的代码。请注意,这是我稍微编辑过的旧代码。

请注意,这段代码很旧,我没有对其进行大量测试,并且也使用了 fs.existSync(这同样不利于生产代码)。

最后说明,如果下载失败,下载的回调会有空有参数,你需要自己检查。

/*download_all
IN_: list
        array of string
        Names of the files to download
     callback
COM: Download all-at-once
*/
function download_all(list, callback){
    var i=0, dltd, dlcp=0;
    dltd=list.length;

    function afterDownload(){
        dlcp=dlcp+1;
        console.log("Telechargement fini:"+dlcp);
        if(dlcp===dltd){
            callback();
        }
    }

    while(i<list.length)
    {
        if(!fs.existsSync(DOWNLOAD_DIR + list[i].substr(list[i].lastIndexOf('/'))))
        {
            //If the file do not exist
            download(list[i], afterDownload);
        } else {
            afterDownload();
        }
        i=i+1;
    }
}

例子:

var http = require('http'),
    DOWNLOAD_DIR = '/home/user/download/',
    list = ['http://somewebsite.com/video.mp4', 'http://somewebsite.com/video2.mp4', 'http://othersite.com/image.png'];

download_all(list, function (){
    //Do stuff
});

【讨论】:

  • 如果您有多个文件需要下载并在触发不同功能之前检查它们是否完成?
  • 这就是它正在做的事情。然而,这是一个古老的答案,使用了已经很老的技术。您应该使用像async 这样的流控制库,或者更好的是Promise 来做到这一点。你想让我更新我的答案吗?
  • 感谢您的回复。我实际上对此有类似的问题。我首先将图像下载到一个文件夹(我不知道它们何时完成)并压缩该文件夹。因为我有多个图像,所以我需要多次调用下载图像函数,这会产生更多的复杂性。 stackoverflow.com/questions/40924340/…
  • @GokhanDilek:你能加入我的node.js chat room吗?您的问题是同步与异步。虽然为您提供正确的代码很容易,但您不会理解背后的概念以及代码失败的原因。我强烈建议您阅读有关事件循环和异步编程的内容。 Here a article about it
  • 谢谢。我现在在房间里。
猜你喜欢
  • 2014-02-03
  • 2013-04-09
  • 2021-07-16
  • 2013-07-20
  • 2011-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-23
相关资源
最近更新 更多