Я использую createWriteStream для загрузки видеофайлов, и когда это будет сделано, я хочу получить размер загруженного файла.

Причина этого в том, что если файл, который он пытается загрузить, не существует, мой код все равно завершается успешно, а createWriteStream вместо этого записывает файл нулевой длины.

Если я могу определить размер файла, я могу определить, является ли его нулевая длина (неудача) или нет (успех).

Вот мой код:

export const downloadTempFileFromUrl = async (downloadUrl, ext) => {

  return new Promise((resolve, reject) => {

    const uuid = uuidv4()
    const fileName = `${uuid}.${ext}`
    const filePath = path.resolve(os.tmpdir(), fileName)

    const writeStream = createWriteStream(filePath)

    const htt = (downloadUrl.startsWith('https')) ? https : http
    htt.get(downloadUrl, response => response.pipe(writeStream))

    writeStream.on('close', () => resolve({ success: true, fileName, filePath, uuid, downloadUrl, writeStream }))
    writeStream.on('error', () => reject({ success: false, fileName, filePath, uuid, downloadUrl, writeStream }))
  })
}

И код вызова просто:

const response = await downloadTempFileFromUrl('http://server/path', 'mp4')
// how do I get the file size?
if (response.success) console.log(response.writeStream)

Заранее спасибо

0
danday74 18 Янв 2022 в 14:37
1
Вы пытались сделать let nonZero = false; writeStream.on('data', () => nonZero = true);, а затем resolve({ success: nonZero, ...});? Таким образом, если событие data запускается хотя бы один раз, это означает, что поток не пуст, и файл также не пуст. Будет ли это работать?
 – 
Sebastian Kaczmarek
18 Янв 2022 в 15:02
Звучит очень осуществимо - пожалуйста, добавьте это как ответ - я уверен, что это сработает - сейчас я просто собираюсь сломаться, но через час попробую - большое спасибо за идею
 – 
danday74
18 Янв 2022 в 15:06

3 ответа

Лучший ответ

Чтобы получить размер файла в Node.js, вы можете использовать метод stat(), предоставляемый встроенным модулем fs. Этот метод работает асинхронно и выводит статистику файла по заданному пути.

Все, что вам нужно сделать, это передать путь к файлу и функцию обратного вызова в метод fs.stat(). Как только Node.js заполнит статистику файла, он вызовет функцию обратного вызова с двумя аргументами: сообщением об ошибке и статистикой файла:

const fs = require('fs');

// Read file stats
fs.stat('file.txt', (err, stats) => {
    if (err) {
        console.log(`File doesn't exist.`);
    } else {
        console.log(stats);
    }
});

Объект статистики, возвращаемый функцией fs.stat(), выглядит следующим образом:

Stats {
  dev: 16777221,
  mode: 33279,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 5172976,
  size: 290,
  blocks: 8,
  atimeMs: 1606143471354.2327,
  mtimeMs: 1599218860000,
  ctimeMs: 1606074010041.4927,
  birthtimeMs: 1605423684000,
  atime: 2020-11-23T14:57:51.354Z,
  mtime: 2020-09-04T11:27:40.000Z,
  ctime: 2020-11-22T19:40:10.041Z,
  birthtime: 2020-11-15T07:01:24.000Z
}

Чтобы получить размер файла, вы можете использовать свойство size в объекте статистики:

const size = stats.size; // 290 bytes
2
nermineslimane 18 Янв 2022 в 15:07
1
Отличный ответ, спасибо +1, очень лаконично - я надеялся сделать это без чтения файла с диска, но приму, если другая идея, данная в комментариях, не сработает
 – 
danday74
18 Янв 2022 в 15:09

Вы можете попробовать определить, был ли поток пуст, прослушав событие data и изменив флаг, если событие было запущено. Нравится:

let nonZero = false;
writeStream.on('data', () => nonZero = true);

А затем выполните обещание, используя флаг nonZero:

resolve({ success: nonZero, ... });

Таким образом, если событие data запускается хотя бы один раз, это означает, что поток не пуст, и файл тоже не пуст.

1
Sebastian Kaczmarek 18 Янв 2022 в 15:11
1
Решил ненадолго пропустить обед и попробовать это сейчас - отличная идея, кстати - если это сработает, плохо приму, а если нет, то в любом случае это была отличная идея;)
 – 
danday74
18 Янв 2022 в 15:12
Спасибо! :D Дайте мне знать, если это работает
 – 
Sebastian Kaczmarek
18 Янв 2022 в 15:14
Извините за задержку, сложности с блокировкой Google Cloud Storage - довольно безумно кажется, что для потока записи нет события данных - нет сигары, но хорошая мысль :)
 – 
danday74
18 Янв 2022 в 17:09

Ответ, который я принял, правильный, но, на мой взгляд, слишком общий. Вот ответ, относящийся к createWriteStream. Этот щенок работает.

Основное отличие от того, что у меня было ранее, заключается в использовании события finish вместо события close. Я читал, что это событие срабатывает только при успехе, тогда как событие close также срабатывает при ошибке. Не то, чтобы это имело большое значение.

Затем, когда срабатывает событие finish, код получает статистику файла перед разрешением. Стоит отметить, что Windows сообщает о файле нулевой длины с небольшой длиной - например. 68 байт - поэтому при обнаружении файлов нулевой длины вам необходимо разрешить это.

export const downloadTempFileFromUrl = async (downloadUrl, ext) => {

  return new Promise((resolve, reject) => {

    const uuid = uuidv4()
    const fileName = `${uuid}.${ext}`
    const filePath = path.resolve(os.tmpdir(), fileName)

    const writeStream = createWriteStream(filePath)

    const htt = (downloadUrl.startsWith('https')) ? https : http
    htt.get(downloadUrl, response => response.pipe(writeStream))

    writeStream.on('finish', () => {
      const VERY_SMALL_FILE_SIZE = 1000 // on Windows zero length files are reported to have a very small size - e.g. 68 bytes
      const statsResponse = appStatSync(filePath)
      const success = statsResponse.success && statsResponse.stats.size > VERY_SMALL_FILE_SIZE
      resolve({ success, fileName, filePath, uuid, downloadUrl, writeStream, stats: statsResponse.stats })
    })
    writeStream.on('error', () => reject({ success: false, fileName, filePath, uuid, downloadUrl, writeStream, stats: null }))
  })
}

AppStatSync — это просто простая оболочка:

import { statSync } from 'fs'
import { logger } from './logger.js'

export const appStatSync = filePath => {
  try {
    const stats = statSync(filePath)
    return { success: true, stats, err: null }
  } catch (err) {
    logger.error('statSync error')
    logger.error(err)
    return { success: false, stats: null, err }
  }
}

Надеюсь, это кому-то поможет!

0
danday74 18 Янв 2022 в 19:30