Вопрос проверяет понимание HTTP Range-запросов и их применения для эффективной загрузки или скачивания больших файлов по частям.
Range-запросы — это стандартный механизм протокола HTTP, определённый в RFC 7233, который позволяет клиенту запрашивать у сервера не весь ресурс целиком, а только указанный диапазон байтов. Это фундаментальная технология для эффективной работы с большими файлами в сети.
Клиент, желающий получить часть файла, добавляет в свой GET-запрос заголовок Range. Значение этого заголовка указывает один или несколько байтовых диапазонов. Например:
Range: bytes=0-499 — запрашивает первые 500 байт файла.Range: bytes=500-999 — запрашивает второй блок из 500 байт.Range: bytes=-500 — запрашивает последние 500 байт файла.Range: bytes=500- — запрашивает всё, начиная с 500-го байта и до конца файла.Если сервер поддерживает Range-запросы, он отвечает статусом 206 Partial Content вместо обычного 200 OK. В ответе обязательно присутствует заголовок Content-Range, который указывает, какой именно диапазон был отправлен, и общий размер файла, например: Content-Range: bytes 0-499/12345. Тело ответа содержит только запрошенные байты.
Эта технология используется в нескольких ключевых сценариях:
// Пример использования Fetch API для загрузки первых 1024 байт файла
async function fetchFileRange(url, start, end) {
const headers = new Headers();
headers.append('Range', `bytes=${start}-${end}`);
const response = await fetch(url, { headers });
if (response.status === 206) {
// Успешно получена часть файла
const contentRange = response.headers.get('Content-Range');
console.log(`Получен диапазон: ${contentRange}`);
const blob = await response.blob();
return blob;
} else {
// Сервер не поддерживает Range-запросы, вернул весь файл (статус 200)
throw new Error('Сервер не поддерживает частичную загрузку');
}
}
// Использование: загружаем первые 1024 байта
// fetchFileRange('https://example.com/large-video.mp4', 0, 1023);
const express = require('express');
const fs = require('fs').promises;
const path = require('path');
const app = express();
app.get('/download/:file', async (req, res) => {
const filePath = path.join(__dirname, 'uploads', req.params.file);
try {
const stat = await fs.stat(filePath);
const fileSize = stat.size;
// Получаем заголовок Range от клиента
const range = req.headers.range;
if (range) {
// Парсим диапазон, например "bytes=0-999"
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
const chunkSize = (end - start) + 1;
const file = await fs.open(filePath, 'r');
const buffer = Buffer.alloc(chunkSize);
await file.read(buffer, 0, chunkSize, start);
await file.close();
// Отправляем частичный контент
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunkSize,
'Content-Type': 'application/octet-stream'
});
res.end(buffer);
} else {
// Если заголовка Range нет, отправляем весь файл
res.writeHead(200, {
'Content-Length': fileSize,
'Content-Type': 'application/octet-stream'
});
const fileStream = fs.createReadStream(filePath);
fileStream.pipe(res);
}
} catch (err) {
res.sendStatus(404);
}
});
app.listen(3000);
Вывод: Range-запросы следует применять везде, где возможна работа с большими файлами — в системах загрузки файлов, стриминговых сервисах, облачных хранилищах. Они значительно улучшают пользовательский опыт, позволяя возобновлять прерванные загрузки и эффективно передавать медиаконтент, экономя трафик и серверные ресурсы.
Уровень
Рейтинг:
3
Сложность:
5
Навыки
Node.js
Networks
Ключевые слова
Подпишись на React Developer в телеграм