iOS-Can';t从解析后端流式传输视频
最近,我在heroku上创建了自己的解析服务器,使用mongoLab存储数据 我的问题是,我正在将视频保存为parseiOS-Can';t从解析后端流式传输视频,ios,video,streaming,pffile,parse-server,Ios,Video,Streaming,Pffile,Parse Server,最近,我在heroku上创建了自己的解析服务器,使用mongoLab存储数据 我的问题是,我正在将视频保存为parsePFFile,但保存后似乎无法对其进行流式处理 以下是我的具体步骤 首先,我保存UIImagePicker //Get the video URL let videoURL = info[UIImagePickerControllerMediaURL] as? NSURL //Create PFFile with NSData from URL let data = NSDat
PFFile
,但保存后似乎无法对其进行流式处理
以下是我的具体步骤
首先,我保存UIImagePicker
//Get the video URL
let videoURL = info[UIImagePickerControllerMediaURL] as? NSURL
//Create PFFile with NSData from URL
let data = NSData(contentsOfURL: videoURL!)
videoFile = PFFile(data: data!, contentType: "video/mp4")
//Save PFFile first, then save the PFUser
PFUser.currentUser()?.setObject(videoFile!, forKey: "profileVideo")
videoFile?.saveInBackgroundWithBlock({ (succeeded, error) -> Void in
print("saved video")
PFUser.currentUser()?.saveInBackgroundWithBlock({ (succeeded, error) -> Void in
if succeeded && error == nil {
print("user saved")
//Hide progress bar
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.progressBar.alpha = 0
}, completion: { (bool) -> Void in
self.progressBar.removeFromSuperview()
})
}else{
//Show error if the save failed
let message = error!.localizedDescription
let alert = UIAlertController(title: "Uploading profile picture error!", message: message, preferredStyle: UIAlertControllerStyle.Alert)
let dismiss = UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(dismiss)
self.presentViewController(alert, animated: true, completion: nil)
}
})
}, progressBlock: { (progress) -> Void in
self.progressBar.setProgress(Float(progress)/100, animated: true)
})
这一切都很好。问题在于当我检索PFFile
并尝试流式传输视频时。以下是我的代码:
//Get URL from my current user
self.videoFile = PFUser.currentUser()?.objectForKey("profileVideo") as? PFFile
self.profileVideoURL = NSURL(string: (self.videoFile?.url)!)
//Create AVPlayerController
let playerController = AVPlayerViewController()
//Set AVPlayer URL to where the file is stored on the sever
let avPlayer = AVPlayer(URL: self.profileVideoURL)
playerController.player = avPlayer
//Present the playerController
self.presentViewController(playerController, animated: true, completion: { () -> Void in
playerController.player?.play()
})
当我展示playerController
时,结果是:
为什么在我尝试流式传输视频时会发生这种情况
非常感谢您的帮助
更新
我最近尝试使用以下代码行播放从其他数据库保存的视频:let videoURL=NSURL(字符串:https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4“”
这确认是我保存的PFFile
格式导致了错误
由于“video/mp4”
的格式可能不正确,因此导致错误的一定是此行:videoFile=PFFile(数据:data!,内容类型:“video/mp4”)
更新2
我在mongoLab上找到了我的.mp4文件的直接链接,发现我可以在google chrome上玩,但不能在safari或我的iPhone上玩
更新3
我发现这是解析api本身的问题,与代码无关,因为当使用原始解析后端(即将关闭的后端)而不是自定义解析服务器时,我的代码可以完美地工作。我目前没有解决方案,但它应该会随着时间的推移得到修复。我有一个带有解析服务器的本地mongodb,需要它来工作: 但我不知道非本地数据库是否也一样。这段代码对我来说很有用
let playerController = AVPlayerViewController()
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
file!.getDataInBackgroundWithBlock({
(movieData: NSData?, error: NSError?) -> Void in
if (error == nil) {
let filemanager = NSFileManager.defaultManager()
let documentsPath : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let destinationPath:NSString = documentsPath.stringByAppendingString("/file.mov")
movieData!.writeToFile ( destinationPath as String, atomically:true)
let playerItem = AVPlayerItem(asset: AVAsset(URL: NSURL(fileURLWithPath: destinationPath as String)))
let player = AVPlayer(playerItem: playerItem)
playerController.player = player
player.play()
} else {
print ("error on getting movie data \(error?.localizedDescription)")
}
})
parse server似乎不支持Safari/iOS中的流媒体,解决方案是使用express&GridStore启用它,如下所示: 解析服务器示例\节点\模块\解析服务器\库\路由器\文件路由器
{
key: 'getHandler',
value: function getHandler(req, res, content) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var video = '.mp4'
var lastFourCharacters = video.substr(video.length - 4);
if (lastFourCharacters == '.mp4') {
filesController.handleVideoStream(req, res, filename).then(function (data) {
}).catch(function (err) {
console.log('404FilesRouter');
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}else{
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.end(data);
}).catch(function (err) {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}
} , ...
_createClass(FilesController, [{
key: 'getFileData',
value: function getFileData(config, filename) {
return this.adapter.getFileData(filename);
}
},{
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this.adapter.handleVideoStream(req, res, filename);
}
}, ...
... , {
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this._connect().then(function (database) {
return _mongodb.GridStore.exist(database, filename).then(function () {
var gridStore = new _mongodb.GridStore(database, filename, 'r');
gridStore.open(function(err, GridFile) {
if(!GridFile) {
res.send(404,'Not Found');
return;
}
console.log('filename');
StreamGridFile(GridFile, req, res);
});
});
})
}
}, ...
function StreamGridFile(GridFile, req, res) {
var buffer_size = 1024 * 1024;//1024Kb
if (req.get('Range') != null) { //was: if(req.headers['range'])
// Range request, partialle stream the file
console.log('Range Request');
var parts = req.get('Range').replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
var start = partialstart ? parseInt(partialstart, 10) : 0;
var end = partialend ? parseInt(partialend, 10) : GridFile.length - 1;
var chunksize = (end - start) + 1;
if(chunksize == 1){
start = 0;
partialend = false;
}
if(!partialend){
if(((GridFile.length-1) - start) < (buffer_size) ){
end = GridFile.length - 1;
}else{
end = start + (buffer_size);
}
chunksize = (end - start) + 1;
}
if(start == 0 && end == 2){
chunksize = 1;
}
res.writeHead(206, {
'Cache-Control': 'no-cache',
'Content-Range': 'bytes ' + start + '-' + end + '/' + GridFile.length,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
});
GridFile.seek(start, function () {
// get GridFile stream
var stream = GridFile.stream(true);
var ended = false;
var bufferIdx = 0;
var bufferAvail = 0;
var range = (end - start) + 1;
var totalbyteswanted = (end - start) + 1;
var totalbyteswritten = 0;
// write to response
stream.on('data', function (buff) {
bufferAvail += buff.length;
//Ok check if we have enough to cover our range
if(bufferAvail < range) {
//Not enough bytes to satisfy our full range
if(bufferAvail > 0)
{
//Write full buffer
res.write(buff);
totalbyteswritten += buff.length;
range -= buff.length;
bufferIdx += buff.length;
bufferAvail -= buff.length;
}
}
else{
//Enough bytes to satisfy our full range!
if(bufferAvail > 0) {
var buffer = buff.slice(0,range);
res.write(buffer);
totalbyteswritten += buffer.length;
bufferIdx += range;
bufferAvail -= range;
}
}
if(totalbyteswritten >= totalbyteswanted) {
// totalbytes = 0;
GridFile.close();
res.end();
this.destroy();
}
});
});
}else{
// res.end(GridFile);
// stream back whole file
res.header('Cache-Control', 'no-cache');
res.header('Connection', 'keep-alive');
res.header("Accept-Ranges", "bytes");
res.header('Content-Type', 'video/mp4');
res.header('Content-Length', GridFile.length);
var stream = GridFile.stream(true).pipe(res);
}
};
{
key: 'getHandler',
value: function getHandler(req, res) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var contentType = _mime2.default.lookup(filename);
if (isFileStreamable(req, filesController)) {
filesController.getFileStream(config, filename).then(function (stream) {
handleFileStream(stream, req, res, contentType);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
} else {
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.set('Content-Type', contentType);
res.set('Content-Length', data.length);
res.end(data);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}},
解析服务器示例\node\u模块\parse server\lib\Controllers\filecontroller
{
key: 'getHandler',
value: function getHandler(req, res, content) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var video = '.mp4'
var lastFourCharacters = video.substr(video.length - 4);
if (lastFourCharacters == '.mp4') {
filesController.handleVideoStream(req, res, filename).then(function (data) {
}).catch(function (err) {
console.log('404FilesRouter');
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}else{
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.end(data);
}).catch(function (err) {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}
} , ...
_createClass(FilesController, [{
key: 'getFileData',
value: function getFileData(config, filename) {
return this.adapter.getFileData(filename);
}
},{
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this.adapter.handleVideoStream(req, res, filename);
}
}, ...
... , {
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this._connect().then(function (database) {
return _mongodb.GridStore.exist(database, filename).then(function () {
var gridStore = new _mongodb.GridStore(database, filename, 'r');
gridStore.open(function(err, GridFile) {
if(!GridFile) {
res.send(404,'Not Found');
return;
}
console.log('filename');
StreamGridFile(GridFile, req, res);
});
});
})
}
}, ...
function StreamGridFile(GridFile, req, res) {
var buffer_size = 1024 * 1024;//1024Kb
if (req.get('Range') != null) { //was: if(req.headers['range'])
// Range request, partialle stream the file
console.log('Range Request');
var parts = req.get('Range').replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
var start = partialstart ? parseInt(partialstart, 10) : 0;
var end = partialend ? parseInt(partialend, 10) : GridFile.length - 1;
var chunksize = (end - start) + 1;
if(chunksize == 1){
start = 0;
partialend = false;
}
if(!partialend){
if(((GridFile.length-1) - start) < (buffer_size) ){
end = GridFile.length - 1;
}else{
end = start + (buffer_size);
}
chunksize = (end - start) + 1;
}
if(start == 0 && end == 2){
chunksize = 1;
}
res.writeHead(206, {
'Cache-Control': 'no-cache',
'Content-Range': 'bytes ' + start + '-' + end + '/' + GridFile.length,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
});
GridFile.seek(start, function () {
// get GridFile stream
var stream = GridFile.stream(true);
var ended = false;
var bufferIdx = 0;
var bufferAvail = 0;
var range = (end - start) + 1;
var totalbyteswanted = (end - start) + 1;
var totalbyteswritten = 0;
// write to response
stream.on('data', function (buff) {
bufferAvail += buff.length;
//Ok check if we have enough to cover our range
if(bufferAvail < range) {
//Not enough bytes to satisfy our full range
if(bufferAvail > 0)
{
//Write full buffer
res.write(buff);
totalbyteswritten += buff.length;
range -= buff.length;
bufferIdx += buff.length;
bufferAvail -= buff.length;
}
}
else{
//Enough bytes to satisfy our full range!
if(bufferAvail > 0) {
var buffer = buff.slice(0,range);
res.write(buffer);
totalbyteswritten += buffer.length;
bufferIdx += range;
bufferAvail -= range;
}
}
if(totalbyteswritten >= totalbyteswanted) {
// totalbytes = 0;
GridFile.close();
res.end();
this.destroy();
}
});
});
}else{
// res.end(GridFile);
// stream back whole file
res.header('Cache-Control', 'no-cache');
res.header('Connection', 'keep-alive');
res.header("Accept-Ranges", "bytes");
res.header('Content-Type', 'video/mp4');
res.header('Content-Length', GridFile.length);
var stream = GridFile.stream(true).pipe(res);
}
};
{
key: 'getHandler',
value: function getHandler(req, res) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var contentType = _mime2.default.lookup(filename);
if (isFileStreamable(req, filesController)) {
filesController.getFileStream(config, filename).then(function (stream) {
handleFileStream(stream, req, res, contentType);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
} else {
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.set('Content-Type', contentType);
res.set('Content-Length', data.length);
res.end(data);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}},
解析服务器示例\node\u模块\parse server\lib\Adapters\Files\GridStoreAdapter
{
key: 'getHandler',
value: function getHandler(req, res, content) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var video = '.mp4'
var lastFourCharacters = video.substr(video.length - 4);
if (lastFourCharacters == '.mp4') {
filesController.handleVideoStream(req, res, filename).then(function (data) {
}).catch(function (err) {
console.log('404FilesRouter');
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}else{
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.end(data);
}).catch(function (err) {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}
} , ...
_createClass(FilesController, [{
key: 'getFileData',
value: function getFileData(config, filename) {
return this.adapter.getFileData(filename);
}
},{
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this.adapter.handleVideoStream(req, res, filename);
}
}, ...
... , {
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this._connect().then(function (database) {
return _mongodb.GridStore.exist(database, filename).then(function () {
var gridStore = new _mongodb.GridStore(database, filename, 'r');
gridStore.open(function(err, GridFile) {
if(!GridFile) {
res.send(404,'Not Found');
return;
}
console.log('filename');
StreamGridFile(GridFile, req, res);
});
});
})
}
}, ...
function StreamGridFile(GridFile, req, res) {
var buffer_size = 1024 * 1024;//1024Kb
if (req.get('Range') != null) { //was: if(req.headers['range'])
// Range request, partialle stream the file
console.log('Range Request');
var parts = req.get('Range').replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
var start = partialstart ? parseInt(partialstart, 10) : 0;
var end = partialend ? parseInt(partialend, 10) : GridFile.length - 1;
var chunksize = (end - start) + 1;
if(chunksize == 1){
start = 0;
partialend = false;
}
if(!partialend){
if(((GridFile.length-1) - start) < (buffer_size) ){
end = GridFile.length - 1;
}else{
end = start + (buffer_size);
}
chunksize = (end - start) + 1;
}
if(start == 0 && end == 2){
chunksize = 1;
}
res.writeHead(206, {
'Cache-Control': 'no-cache',
'Content-Range': 'bytes ' + start + '-' + end + '/' + GridFile.length,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
});
GridFile.seek(start, function () {
// get GridFile stream
var stream = GridFile.stream(true);
var ended = false;
var bufferIdx = 0;
var bufferAvail = 0;
var range = (end - start) + 1;
var totalbyteswanted = (end - start) + 1;
var totalbyteswritten = 0;
// write to response
stream.on('data', function (buff) {
bufferAvail += buff.length;
//Ok check if we have enough to cover our range
if(bufferAvail < range) {
//Not enough bytes to satisfy our full range
if(bufferAvail > 0)
{
//Write full buffer
res.write(buff);
totalbyteswritten += buff.length;
range -= buff.length;
bufferIdx += buff.length;
bufferAvail -= buff.length;
}
}
else{
//Enough bytes to satisfy our full range!
if(bufferAvail > 0) {
var buffer = buff.slice(0,range);
res.write(buffer);
totalbyteswritten += buffer.length;
bufferIdx += range;
bufferAvail -= range;
}
}
if(totalbyteswritten >= totalbyteswanted) {
// totalbytes = 0;
GridFile.close();
res.end();
this.destroy();
}
});
});
}else{
// res.end(GridFile);
// stream back whole file
res.header('Cache-Control', 'no-cache');
res.header('Connection', 'keep-alive');
res.header("Accept-Ranges", "bytes");
res.header('Content-Type', 'video/mp4');
res.header('Content-Length', GridFile.length);
var stream = GridFile.stream(true).pipe(res);
}
};
{
key: 'getHandler',
value: function getHandler(req, res) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var contentType = _mime2.default.lookup(filename);
if (isFileStreamable(req, filesController)) {
filesController.getFileStream(config, filename).then(function (stream) {
handleFileStream(stream, req, res, contentType);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
} else {
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.set('Content-Type', contentType);
res.set('Content-Length', data.length);
res.end(data);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}},
GridStore适配器的底部
{
key: 'getHandler',
value: function getHandler(req, res, content) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var video = '.mp4'
var lastFourCharacters = video.substr(video.length - 4);
if (lastFourCharacters == '.mp4') {
filesController.handleVideoStream(req, res, filename).then(function (data) {
}).catch(function (err) {
console.log('404FilesRouter');
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}else{
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.end(data);
}).catch(function (err) {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}
} , ...
_createClass(FilesController, [{
key: 'getFileData',
value: function getFileData(config, filename) {
return this.adapter.getFileData(filename);
}
},{
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this.adapter.handleVideoStream(req, res, filename);
}
}, ...
... , {
key: 'handleVideoStream',
value: function handleVideoStream(req, res, filename) {
return this._connect().then(function (database) {
return _mongodb.GridStore.exist(database, filename).then(function () {
var gridStore = new _mongodb.GridStore(database, filename, 'r');
gridStore.open(function(err, GridFile) {
if(!GridFile) {
res.send(404,'Not Found');
return;
}
console.log('filename');
StreamGridFile(GridFile, req, res);
});
});
})
}
}, ...
function StreamGridFile(GridFile, req, res) {
var buffer_size = 1024 * 1024;//1024Kb
if (req.get('Range') != null) { //was: if(req.headers['range'])
// Range request, partialle stream the file
console.log('Range Request');
var parts = req.get('Range').replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
var start = partialstart ? parseInt(partialstart, 10) : 0;
var end = partialend ? parseInt(partialend, 10) : GridFile.length - 1;
var chunksize = (end - start) + 1;
if(chunksize == 1){
start = 0;
partialend = false;
}
if(!partialend){
if(((GridFile.length-1) - start) < (buffer_size) ){
end = GridFile.length - 1;
}else{
end = start + (buffer_size);
}
chunksize = (end - start) + 1;
}
if(start == 0 && end == 2){
chunksize = 1;
}
res.writeHead(206, {
'Cache-Control': 'no-cache',
'Content-Range': 'bytes ' + start + '-' + end + '/' + GridFile.length,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
});
GridFile.seek(start, function () {
// get GridFile stream
var stream = GridFile.stream(true);
var ended = false;
var bufferIdx = 0;
var bufferAvail = 0;
var range = (end - start) + 1;
var totalbyteswanted = (end - start) + 1;
var totalbyteswritten = 0;
// write to response
stream.on('data', function (buff) {
bufferAvail += buff.length;
//Ok check if we have enough to cover our range
if(bufferAvail < range) {
//Not enough bytes to satisfy our full range
if(bufferAvail > 0)
{
//Write full buffer
res.write(buff);
totalbyteswritten += buff.length;
range -= buff.length;
bufferIdx += buff.length;
bufferAvail -= buff.length;
}
}
else{
//Enough bytes to satisfy our full range!
if(bufferAvail > 0) {
var buffer = buff.slice(0,range);
res.write(buffer);
totalbyteswritten += buffer.length;
bufferIdx += range;
bufferAvail -= range;
}
}
if(totalbyteswritten >= totalbyteswanted) {
// totalbytes = 0;
GridFile.close();
res.end();
this.destroy();
}
});
});
}else{
// res.end(GridFile);
// stream back whole file
res.header('Cache-Control', 'no-cache');
res.header('Connection', 'keep-alive');
res.header("Accept-Ranges", "bytes");
res.header('Content-Type', 'video/mp4');
res.header('Content-Length', GridFile.length);
var stream = GridFile.stream(true).pipe(res);
}
};
{
key: 'getHandler',
value: function getHandler(req, res) {
var config = new _Config2.default(req.params.appId);
var filesController = config.filesController;
var filename = req.params.filename;
var contentType = _mime2.default.lookup(filename);
if (isFileStreamable(req, filesController)) {
filesController.getFileStream(config, filename).then(function (stream) {
handleFileStream(stream, req, res, contentType);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
} else {
filesController.getFileData(config, filename).then(function (data) {
res.status(200);
res.set('Content-Type', contentType);
res.set('Content-Length', data.length);
res.end(data);
}).catch(function () {
res.status(404);
res.set('Content-Type', 'text/plain');
res.end('File not found.');
});
}
}},
函数StreamGridFile(GridFile,req,res){
var buffer_size=1024*1024;//1024Kb
if(req.get('Range')!=null){//was:if(req.headers['Range']))
//范围请求,部分流文件
log(“范围请求”);
var parts=req.get('Range')。replace(/bytes=/,“”)。split(“”);
var partialstart=parts[0];
var partialend=零件[1];
var start=partialstart?parseInt(partialstart,10):0;
var end=partialend?parseInt(partialend,10):GridFile.length-1;
var chunksize=(结束-开始)+1;
if(chunksize==1){
开始=0;
partialend=false;
}
如果(!partialend){
if(((GridFile.length-1)-开始)<(缓冲区大小)){
end=GridFile.length-1;
}否则{
结束=开始+(缓冲区大小);
}
chunksize=(结束-开始)+1;
}
如果(开始==0&&end==2){
chunksize=1;
}
文书标题(206{
“缓存控制”:“无缓存”,
“内容范围”:“字节数”+start+'-'+end+'/'+GridFile.length,
“接受范围”:“字节”,
“内容长度”:chunksize,
“内容类型”:“视频/mp4”,
});
seek(开始,函数(){
//获取网格文件流
var stream=GridFile.stream(true);
var=false;
var bufferIdx=0;
var=0;
变量范围=(结束-开始)+1;
var totalbyteswanted=(结束-开始)+1;
var TotalBytesWrited=0;
//回信
stream.on('data',函数(buff){
bufferAvail+=buff.length;
//好的,检查一下我们是否有足够的货物覆盖我们的范围
if(bufferAvail<范围){
//没有足够的字节来满足我们的全部范围
如果(bufferAvail>0)
{
//写满缓冲区
res.write(buff);
TotalBytesWrited+=buff.length;
范围-=浅黄色长度;
bufferIdx+=buff.length;
bufferAvail-=buff.length;
}
}
否则{
//足够的字节满足我们的全部范围!
如果(bufferAvail>0){
var buffer=buff.slice(0,范围);
res.write(缓冲区);
TotalBytesWrited+=buffer.length;
bufferIdx+=范围;
bufferAvail-=范围;
}
}
如果(TotalBytesWrited>=TotalBytesWasted){
//totalbytes=0;
close();
res.end();
这个。销毁();
}
});
});
}否则{
//res.end(GridFile);
//流回整个文件
res.header('Cache-Control','no-Cache');
res.header('连接','保持活动');
res.header(“接受范围”、“字节”);
res.header(“内容类型”、“视频/mp4”);
res.header('Content-Length',GridFile.Length);
var stream=GridFile.stream(true).pipe(res);
}
};
附言
原始答案由@Bragegs这里给出-。User@Stav1在这个帖子中也提到了这一点,但不幸的是,他被否决了?对于仍然在这里登陆的任何人来说,在查看新的解析服务器示例时,更新解析服务器现在识别流媒体;但是,您应该使用Parse iOS sdk方法来检索视频。下面是服务器代码,以防您推出自定义解析服务器。下面是一些流媒体方法的列表 在中发现服务器代码更改: parse-server-e