Проблема с запросами с сервера node Js
Пытаюсь сделать свой пакет telegram Бота для удобства написания его через Node JS.
Если увидите ошибки в моем коде, пожалуйста, сообщите сразу, так как это мой первый большой ООП проект и я ток учусь)
После того как я прочитал что библиотека request устарела, я задался вопросом как мне делать запросы на сайт с новыми фичами.
Так же, возникла проблема с загрузкой mp3 файла с сервера "Последние 4 строки кода", сохраненный файл не удается запустить. Как можно корректно скачать файл на сервер?
"use strict"
let request = require("request-promise");
class TelegramAPI {
constructor(token) {
this.token = token;
this.lastUpdateId = undefined;
}
buildURL(method) {
let url = `https://api.telegram.org/bot${this.token}/${method}`;
return url;
}
getMe() {
}
getUpdate() {
let data = {
uri: this.buildURL("getUpdates"),
body: {
offset: this.lastUpdateId
},
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
json: true
}
request(data).then(response => {
console.log(response);
}).catch(err => {
console.log(err);
});
}
on(type, callBack) {
let data = {
uri: this.buildURL("getUpdates"),
body: {
offset: this.lastUpdateId
},
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
json: true
}
request(data).then((request) => {
let value = request.result;
if (value[0]) {
this.lastUpdateId = ++value[value.length - 1].update_id;
}
for (let i = 0; i < value.length; i++) {
if (!value[i].edited_message && value[i].message.text && type === "message") {
callBack(value[i].message);
} else if (value[i].edited_message && type === "editMessage") {
callBack(value[i].edited_message);
} else if (!value[i].edited_message && value[i].message.poll && type === "poll") {
callBack(value[i].message);
}
}
setTimeout(() => { this.on(type, callBack) }, 1000);
})
}
sendMessage(id, text, settings = { "pagePreview": false, "disNotification": false, "replyMessageId": null }) {
let url = this.buildURL("sendMessage");
let data = {
uri: url,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: {
"chat_id": id,
"text": text,
"disable_web_page_preview": settings.pagePreview || false,
"disable_notification": settings.disNotification || false,
"reply_to_message_id": settings.replyMessageId
},
json: true
}
request(data).catch(err => {
console.log(err);
});
}
sendAudio(id, file, settings = { performer: "Исполнитель не указан", title: "Неизвестное название" }) {
let url = this.buildURL("sendAudio");
let data = {
uri: url,
body: {
"chat_id": id,
"audio": file,
"performer": settings.performer,
"title": settings.title
},
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
json: true
}
}
}
let file = fs.createWriteStream("music.mp3");
request("https://zaycev.net/musicset/dl/8ef61c696365a43c99831a8e37a128a5/4362299.json").then(response => {
file.write(response);
file.end();
});```
Ответы (1 шт):
Как можно корректно скачать файл на сервер?
Сохранить файл по заданному URL можно таким способом:
(данный способ работает с перенаправлением, прогресс-индикатором и автоматическим вычислением имени загружаемого файла)
const http = require('http');
const https = require('https');
const fs = require('fs');
// Пытается вычислить имя файла по заданному URL
function getFilenameFromLink(link) {
const filename = link.replace(/\?.*$/,'').match(/[^\/]+$/);
return Array.isArray(filename) ? filename.shift() : filename;
}
// Пытается вычислить имя файла из хедера или URL
function getFilename(headers, url) {
let filename = getFilenameFromLink(url);
let ext = '';
let match;
if (headers.hasOwnProperty('content-disposition')) {
if (match = /filename\*?=(\S+)/i.exec(headers['content-disposition'])) {
const [ matchedFullText, fname ] = match;
const encoding = fname.slice(0,5).toLowerCase();
if (encoding == "utf-8") {
filename = decodeURIComponent(fname.slice(7));
} else if (encoding == "utf8'") {
filename = decodeURIComponent(fname.slice(6));
} else {
filename = fname.replace(/[^\w\-\.]/g, '');
}
}
}
if (headers.hasOwnProperty('content-type')) {
ext = headers['content-type'].match(/[^\/]+$/);
if (Array.isArray(ext)) {
ext = ext.shift();
}
if (filename.length && !/\.\w+$/.test(filename)) {
filename += `.${ext}`;
}
}
return filename;
}
function downloadFile(url, dirname, callback) {
const httpModule = url.substr(0, 6) == 'https:' ? https : http;
const request = httpModule.get(url, res => {
// Перенаправление
if (res.statusCode === 302) {
process.stdout.write(`${res.statusCode} GET "${url}"\n`);
// запускаем после окончания текущей операции Event Loop
process.nextTick(downloadFile, res.headers.location, dirname, callback);
}
if (res.statusCode === 200) {
process.stdout.write(`${res.statusCode} GET "${url}"`);
let filename = getFilename(res.headers, url);
if (filename.length > 255) {
// Взять последние 30 символов из имени файла
filename = filename.slice(-30);
}
if (!filename.length) {
filename = new Date().toISOString().replace(/\D/g,'');
}
// инициализируем прогресс
downloadFile.progress = {
delta: 0,
percents: 0,
peak: res.headers.hasOwnProperty('content-length')
? +res.headers['content-length'] : 0
};
if (!downloadFile.hasOwnProperty('stream')) {
// Открываем поток для записи
downloadFile.stream = fs.createWriteStream(`${dirname}/${filename}`, { autoClose: true });
// Сохраняем файл
res.pipe(downloadFile.stream);
}
res.on('data', chunk => {
downloadFile.progress.delta += chunk.length;
const percents = Math.ceil((downloadFile.progress.delta * 100) / downloadFile.progress.peak);
if (downloadFile.progress.percents != percents) {
downloadFile.progress.percents = percents;
const size = calculate(downloadFile.progress.delta);
process.stdout.write(`\x1B[s (~${size}) ${percents}%\x1B[u`);
}
});
// Закрываем поток, очищаем временные данные
res.on('end', function () {
const size = calculate(downloadFile.progress.delta);
process.stdout.write(` (~${size}) ${downloadFile.progress.percents}%\n`);
cleanupAfterDownloadFile();
// Согласно контракту первым аргументом передаем ошибку, вторым - данные
callback(null, filename);
});
}
});
request.on('error', err => {
// Закрываем поток, очищаем временные данные, передаем ошибку
cleanupAfterDownloadFile();
// Согласно контракту первым аргументом передаем ошибку, вторым - данные
callback(err, null);
});
// (!) это важно
request.end();
}
function calculate(bytes) {
// сложно сказать почему не 1024^2, где-то читал, что потоки
// считаются не по 1024 а по 1000.
// когда я использую константу в 1024, размер файла в LINUX
// отличается от показателей индикатора
return bytes < 1000000
? (bytes / 1000).toFixed(1) + 'Kb'
: (bytes / 1000000).toFixed(1) + 'Mb'
;
}
function cleanupAfterDownloadFile() {
if (downloadFile.hasOwnProperty('stream')) {
downloadFile.stream.close();
delete downloadFile.stream;
}
}
// Promise обертка
function downloadFileAsync(url, dirname) {
return new Promise((resolve, reject) => {
downloadFile(url, dirname, function (err, filename) {
err ? reject(err) : resolve(filename);
});
});
}
// пакетная загрузка
function downloadBatch(dirname, list=[]) {
if (list.length) {
downloadFile(list.shift(), dirname, function(err, filename) {
// err ? console.error(err)
// : console.log('Saved to: %s/%s', dirname, filename)
// ;
err && console.error(err);
// дожидаемся очереди и снова пытаемся скачать файл
setImmediate(downloadBatch, dirname, list);
});
} else {
console.log('FINISH!');
}
}
Применение:
Вариант с использованием callback-метода
const url = 'https://...';
const dirname = '.';
downloadFile(url, dirname, function (err, filename) {
if (err) {
return console.error(err);
}
console.log('Saved to: %s/%s', dirname, filename);
});
Вариант с использованием Promise
function processPromise(filename) {
filename && console.log('Saved to: %s/%s', dirname, filename);
if (files.length) {
return downloadFileAsync(files.shift(), dirname);
}
}
const dirname = '.';
const urls = [
'https://...',
'https://...',
'https://...'
];
Promise.resolve()
.then(processPromise)
.then(processPromise)
.then(processPromise)
.catch(console.error)
;
// Или так:
try {
const filename = await downloadFileAsync('https://...', dirname);
filename && console.log('Saved to: %s/%s', dirname, filename);
} catch (err) {
console.error(err);
}
Пакетная загрузка
const dirname = '.';
const list = Array.from(['','','','',''], () => 'https://picsum.photos/1024/768');
downloadBatch(dirname, list);
Затронутые термины: Контрактное программирование, Http-статусы, Перенаправление, Content-Disposition, rfc6266, callback, Promise, Event Loop