我正在使用expressjs
框架,并且已经做到了:
// Readable Streams Storage Class
class FileReadStreams {
constructor() {
this._streams = {};
}
make(file, options = null) {
return options ?
fs.createReadStream(file, options)
: fs.createReadStream(file);
}
get(file) {
return this._streams[file] || this.set(file);
}
set(file) {
return this._streams[file] = this.make(file);
}
}
const readStreams = new FileReadStreams();
// Getting file stats and caching it to avoid disk I/O
function getFileStat(file, callback) {
let cacheKey = ['File', 'stat', file].join(':');
cache.get(cacheKey, function(err, stat) {
if(stat) {
return callback(null, stat);
}
fs.stat(file, function(err, stat) {
if(err) {
return callback(err);
}
cache.set(cacheKey, stat);
callback(null, stat);
});
});
}
// Streaming whole file
function streamFile(file, req, res) {
getFileStat(file, function(err, stat) {
if(err) {
console.error(err);
return res.status(404);
}
let bufferSize = 1024 * 1024;
res.writeHead(200, {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': 0,
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size
});
readStreams.make(file, {bufferSize}).pipe(res);
});
}
// Streaming chunk
function streamFileChunked(file, req, res) {
getFileStat(file, function(err, stat) {
if(err) {
console.error(err);
return res.status(404);
}
let chunkSize = 1024 * 1024;
if(stat.size > chunkSize * 2) {
chunkSize = Math.ceil(stat.size * 0.25);
}
let range = (req.headers.range) ? req.headers.range.replace(/bytes=/, "").split("-") : [];
range[0] = range[0] ? parseInt(range[0], 10) : 0;
range[1] = range[1] ? parseInt(range[1], 10) : range[0] + chunkSize;
if(range[1] > stat.size - 1) {
range[1] = stat.size - 1;
}
range = {start: range[0], end: range[1]};
let stream = readStreams.make(file, range);
res.writeHead(206, {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': 0,
'Content-Type': 'audio/mpeg',
'Accept-Ranges': 'bytes',
'Content-Range': 'bytes ' + range.start + '-' + range.end + '/' + stat.size,
'Content-Length': range.end - range.start + 1,
});
stream.pipe(res);
});
}
router.get('/:file/stream', (req, res) => {
const file = path.join('path/to/mp3/', req.params.file+'.mp3');
if(/firefox/i.test(req.headers['user-agent'])) {
return streamFile(file, req, res);
}
streamFileChunked(file, req, res);
});
网站的完整资源在这里
尝试修复您的代码:
这将强制浏览器对资源进行分块处理。
var header = {
'Content-Length': range[1],
'Content-Type': type,
'Access-Control-Allow-Origin': req.headers.origin || "*",
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'POST, GET, OPTIONS',
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': 0
};
if(/firefox/i.test(req.headers['user-agent'])) {
res.writeHead(200, header);
}
else {
header['Accept-Ranges'] = 'bytes';
header['Content-Range'] = 'bytes ' + range[0] + '-' + range[1] + '/' + total;
header['Content-Length'] = range[2];
res.writeHead(206, header);
}