Aleksei_Segodin
@Aleksei_Segodin
арт директор / дизайнер

Как правильно отправить на сервер и сохранить буфер загружаемого файла?

Привет.
Бьюсь над проблемой уже много часов:
Хочу отправить ZIP файл с компьютера с помощью NodeJS. Отправляю буфер файла методом PUT. И данные передаются. Однако тот файл, который записывается на сервер не соответствует по размеру изначальному файлу.

Вот как я делаю в NodeJS отправку файла:

const buffer = fs.readFileSync(`../source/archive.zip`);
const options = {
    hostname: url.hostname,
    port: 443,
    path: url.pathname,
    method: 'PUT',
    headers: {
        'Content-Type': 'application/zip',
        'Content-Length': buffer.length
    }
};
const req = https.request(options, res => {
    res.on('error', err => console.log('ошибка:', err));
    res.on('end', () => console.log('отправлено'));
});

req.write(buffer);
req.end();


А вот так я принимаю файл на сервере с помощью NodeJS + Express:

router.put('/', (req, res, next) => {

    const pathToFile = path.join(__dirname, '..', 'public', 'uploads', 'archive.zip');

    let chunks = '';

    req.on('data', chunk => {
        chunks += chunk;
    });

    req.on('end', () => {
        fs.writeFile(pathToFile, chunks, { encoding: 'binary' } , (err) => {
            if (err) throw err;
            res.send('файл сохранён');
        });
    });

});


Как я написал выше: сохраняемый файл всегда отличается по размеру от оригинала. При этом, если в функции writeFile я указываю кодировку 'binary', то размер сохранённого файла чуть-чуть меньше оригинального. А если не указываю кодировку, то размер примерно вдвое больше оригинала. В обоих случаях архив получается битым и не разархивируется.

Скажите, что я делаю не так? Весь интернет уже перерыл...

Ещё меня смущает то, что с буфером данных не передаются мета-данные файла. Или всё-таки отправляются, но я не знаю как это проверить.

Заранее спасибо!
  • Вопрос задан
  • 663 просмотра
Решения вопроса 1
bingo347
@bingo347
Crazy on performance...
Во-первых, строки в JS в памяти хранятся в виде utf-16, а значит сохранение произвольного бафера в строку может сломать данные.

Вот тут Ваши данные ломаются:
let chunks = '';

req.on('data', chunk => {
    chunks += chunk;
});


Во-вторых, не стоит читать файлы в память целиком. Память не резиновая, и однажды ее может просто не хватить.

В-третьих, не нужно перекладывать байты из одного потока в другой руками, есть pipe, который делает это гораздо эффективнее.

Отправка:
const filePath = `../source/archive.zip`;
const reader = fs.createReadStream(filePath);
const options = {
    hostname: url.hostname,
    port: 443,
    path: url.pathname,
    method: 'PUT',
    headers: {
        'Content-Type': 'application/zip',
        'Content-Length': fs.statSync(filePath).size
    }
};
const req = https.request(options, res => {
    res.on('error', err => console.log('ошибка:', err));
    res.on('end', () => console.log('отправлено'));
});
reader.pipe(req);

Получение:
router.put('/', (req, res, next) => {
    const pathToFile = path.join(__dirname, '../public/uploads/archive.zip');
    const writer = fs.createWriteStream(pathToFile);
    req.pipe(writer).once('error', next).once('finish', () => {
        res.send('файл сохранён');
    });
});
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы