iOS - Can't stream video from Parse Backend

后端 未结 4 1475
礼貌的吻别
礼貌的吻别 2021-01-05 15:36

Recently I have created my own parse server hosted on heroku using mongoLab to store my data.

My problem is I am saving a video as a parse PFFile, howe

4条回答
  •  说谎
    说谎 (楼主)
    2021-01-05 15:43

    parse-server doesn't seem to be supporting streaming in Safari/iOS and the solution is the enable it using express & GridStore as follows,

    parse-server-example\node_modules\parse-server\lib\Routers\FilesRouter

        {
    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.');
      });
     }
    }
    } , ...
    

    parse-server-example\node_modules\parse-server\lib\Controllers\FilesController

    _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);
    }
    }, ...
    

    parse-server-example\node_modules\parse-server\lib\Adapters\Files\GridStoreAdapter

    ... , {
         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);
          });
          });
          })
          }
          }, ...
    

    Bottom of GridStore Adapter

    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);
       }
      };
    

    P.S The original answer is given by @Bragegs here - https://github.com/ParsePlatform/parse-server/issues/1440#issuecomment-212815625 . User @Stav1 has also mentioned it in this thread, but unfortunately he was downvoted ?

提交回复
热议问题