使用异步在Node JS中一个接一个地下载系列视频。

时间:2019-06-13 05:53:27

标签: javascript node.js async.js

我要一个接一个地下载视频。

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

我具有以下目录结构-

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作为数组,所以我不确定该怎么做。

3 个答案:

答案 0 :(得分:3)

您可以在标准的循环中使用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()

答案 1 :(得分: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');


})

答案 2 :(得分:1)

尝试异步等待。首先下载,然后在同步中编写。

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);
});