【问题标题】:Download videos in series one after the other in Node JS using async?使用异步在 Node JS 中一个接一个地下载视频?
【发布时间】:2019-06-13 16:40:03
【问题描述】:

我想在一个系列中一个接一个地下载视频。

也就是说,第一个应该在第二个开始之前完全下载,第二个应该在第三个开始之前完全下载,依此类推。

我有以下目录结构-

video-downloader
├── index.js
├── videos.js
├── package.json

package.json

{
  "name": "video-downloader",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "download": "^7.1.0"
  },
  "scripts": {
    "start": "node index"
  }
}

video.js

const videos = [
  {
    url: 'https://video.com/lesson1.mp4',
    name: 'Lesson 1',
  },
  {
    url: 'https://video.com/lesson2.mp4',
    name: 'Lesson 2',
  },
  .
  .
  .
  {
    url: 'https://video.com/lesson2.mp4',
    name: 'Lesson 100',
  }
]

index.js

const fs = require('fs')
const download = require('download')

const videos = require('./videos')

const OUTPUT_DIR = 'Downloads'

fs.mkdir(OUTPUT_DIR, () => {
   main()
})

const main = () => {
    videos.map((video, i) => {
        console.log(`Downloaded file ${i + 1} of ${videos.length} (${video.name})`)
        download(video.url).pipe(
            fs.createWriteStream(`${OUTPUT_DIR}/${video.name}.mp4`),
        )
    })
}

这会并行地逐块下载视频。一次下载所有视频,但没有一个在另一个开始之前完成。

如何连续下载?

我知道我应该使用 http://caolan.github.io/async/ 之类的东西,但它需要一个函数签名并且我将 videos 作为一个数组,所以我不知道该怎么做。

【问题讨论】:

    标签: javascript node.js async.js


    【解决方案1】:

    您可以在标准for循环中使用await关键字,事情会按顺序处理,并等待每次下载再继续。

    const fs = require('fs')
    const download = require('download')
    const videos = require('./videos')
    const util = require('util')
    
    const mkdirAsync = util.promisify(fs.mkdir)
    
    const OUTPUT_DIR = 'Downloads'
    
    const main = async () => {
      await mkdirAsync(OUTPUT_DIR)
    
      for (let i = 0; i < videos.length; i++) {
        const video = videos[i]
        const data = await download(video.url)
        fs.writeFileSync(`${OUTPUT_DIR}/${video.name}.mp4`, data)
        console.log(`Downloaded file ${i + 1} of ${videos.length} (${video.name})`)
      }
    }
    
    main()
    

    【讨论】:

    • np。我刚看到你的帖子编辑。你可能想看看承诺你的 mkdir 函数(和任何其他基于回调的函数),这样你就可以防止意大利面条代码。我会这样编辑我的帖子
    • 这有什么不同?我的意思是它现在工作得很好。
    • 功能上没有什么不同。可能是我建议过分了,抱歉。但是正如我已经建议的那样……您在一段代码中使用了两种不同的异步方法-回调和异步/等待。如果你坚持一个,那么推理的精神负担就会减少。单独承诺 fs.mkdir 的示例有点做作 - 但是一旦您添加更多异步操作,它会更容易推断它是仅 async/await 还是仅回调。
    • 哦不,我只是好奇。不需要抱歉。我知道了。我也倾向于使用两者中的任何一个,并且我更喜欢 async/await 而不使用 try-catch,并且我倾向于编写更少的 LOC,所以我会保留我的 LOC,直到它给我带来麻烦。那我可能会试试 urs。再次感谢:)
    【解决方案2】:

    你可以使用.reduce和promise来顺序解析,如下:

    const fs = require('fs')
    const sh = require('shelljs')
    const download = require('download')
    
    const videos = require('./videos')
    
    const OUTPUT_DIR = 'Downloads'
    
    sh.mkdir('-p', OUTPUT_DIR)
    
    videos = videos.reduce((acc, item) => {
    
      return acc.then(() => {
        return new Promise((resolve) => {
    
          // Here you are using it as a Duplex Stream, not a promise,
          // therefore, you must check when the stream emits the 'end' event
          // so you can proceed further
          let stream = download(video.url)
            .pipe(fs.createWriteStream(`${OUTPUT_DIR}/${video.name}.mp4`));
    
          stream.on('end', () => {
            console.log(`stream done ${item}`);
            resolve(item);
          })
    
        })
    
      });
    
    }, Promise.resolve());
    
    // 'videos' is now a promise
    videos.then((lastPromise) => {
    
      // using reduce will return the last evaluated item(promise)
      // but reaching the last one means the promises before that have been resolved
    
      console.log('all files were downloaded');
    
    
    })

    【讨论】:

    • 我没有尝试过这个,因为第一个对我有用,但有一个支持并感谢您的回答:)
    【解决方案3】:

    为此尝试异步等待。先下载再同步写入。

    const fs = require('fs');
    const sh = require('shelljs');
    const download = require('download');
    
    const videos = require('./videos');
    
    const OUTPUT_DIR = 'Downloads';
    
    sh.mkdir('-p', OUTPUT_DIR);
    
    videos.forEach(async (video, i) => {
      console.log(`Downloading ${video.name}. Fil${i + 1}/${videos.length} - `);
      const data = await download(video.url);
      fs.writeFileSync(`${OUTPUT_DIR}/${video.name}.mp4`, data);
    });
    

    【讨论】:

    • 谢谢你这个工作,但它以随机顺序下载。我希望它按照指定的顺序完成 :)
    • 这不行,你会得到一个包含100个promise对象的数组,并且下载仍然是并行的
    • map 不需要因为每个都可以工作,我们不需要承诺数组。它将在每次循环迭代中下载一个内容。检查视频数组的顺序。
    • 确实可以,但按随机顺序下载。即使使用 forEach。使用 for 循环的第一个解决方案效果很好:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多