问题
I'm having a problem loading mp4 video files on Safari being served from my nodejs server. The problem does not exist on Chrome.
To illustrate the problem, I have a simple html webpage which reads an mp4 file mov_bbb.mp4
served from w3schools. I then download the same file and serve it from my nodejs server.
vid1
(served from w3schools) loads fine on both Chrome and Safari.
vid2
(served from my nodejs server) load fine on Chrome but not on Safari.
Here's a screenshot of what I see in Chrome:
Here's a screenshot of what I see in Safari:
Here is my html:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<video id="vid1" controls preload="auto" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
<video id="vid2" controls preload="auto" src="http://localhost:8125/mov_bbb.mp4"></video>
</body>
</html>
Here is my nodejs server:
var http = require('http');
var fs = require('fs');
var path = require('path');
http.createServer(function (request, response) {
console.log('request starting...');
var filePath = '.' + request.url;
if (filePath == './')
filePath = './index.html';
var extname = path.extname(filePath);
var contentType = 'video/mp4';
fs.readFile(filePath, function(error, content) {
if (error) {
if(error.code == 'ENOENT'){
fs.readFile('./404.html', function(error, content) {
response.writeHead(200, { 'Content-Type': contentType });
response.end(content, 'utf-8');
});
}
else {
response.writeHead(500);
response.end('Sorry, check with the site admin for error: '+error.code+' ..\n');
response.end();
}
}
else {
response.writeHead(200, {
});
response.end(content, 'utf-8');
}
});
}).listen(8125);
console.log('Server running at http://127.0.0.1:8125/');
Any suggestions would be much appreciated.
Update:
Revised nodejs code. Still same issue on Safari though:
var http = require('http');
var fs = require('fs');
http.createServer(function (request, response) {
console.log('request starting...');
var filePath = '.' + request.url;
if (filePath == './') {
var contentType = 'text/html';
filePath = './index.html';
} else {
var contentType = 'video/mp4';
}
var rstream = fs.createReadStream(filePath);
rstream.on('error', function(error) {
response.writeHead(404);
response.end();
});
rstream.on('open', function () {
response.writeHead(200, {
'Content-Type': contentType,
});
});
rstream.pipe(response);
}).listen(8125);
console.log('Server running at http://127.0.0.1:8125/');
Also chrome inspector for http headers:
回答1:
The issue with the video playback has to do with handling the Content-Range
range header which must be implemented on the server. Chrome and Safari send different request headers. In my case Chrome sends a single request with range:
bytes=0-
Whereas Safari sends multiple requests:
bytes=0-1
bytes=0-1013150
bytes=197534-1013150
For more information see this thread and for specific code that worked for me see this answer.
回答2:
First off, don't use readFile()
for serving files, it causes unnecessary (potentially high) memory usage because it's loading the entire file into memory. Use fs.createReadStream()
and pipe it to the response
instead.
Secondly and most importantly, a Content-Type
is not being sent in the success case. Also, the Content-Type
is wrong in the error case because text/html data is being sent in the response, not video/mp4 data.
Additionally, arbitrary paths should not be passed to filesystem calls because someone could pass a malicious path (including relative paths to get to the filesystem root) and read sensitive information from your filesystem (e.g. private keys, passwords, etc.).
Lastly, if you're going to write a Buffer
to a response, you don't need to specify an encoding (e.g. 'utf-8'
) because the data is already in Buffer
form.
来源:https://stackoverflow.com/questions/42846947/cant-play-mp4-video-in-safari-served-by-nodejs-server