iOS - Can't stream video from Parse Backend

后端 未结 4 1474
礼貌的吻别
礼貌的吻别 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:42

    I have a local mongodb with parse-server and needed this to make it work:

    Parse-server stream video to IOS from PFFile.url

    Don't know if it is the same with non-local databases though.

    0 讨论(0)
  • 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 ?

    0 讨论(0)
  • For anyone that lands here still, in looking at the new Parse-Server-example update Parse-server now recognizes streaming; however, you should utilize the Parse iOS sdk method to retrieve the video. The server code is below in case you are rolling out a custom parse-server. I follow with a list of some of the streaming methods.

    Server-code change found in:

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

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

    Method example for streaming with iOS parse sdk(swift):

    vidObject.video.getDataStreamInBackground(block: <#T##PFDataStreamResultBlock?##PFDataStreamResultBlock?##(InputStream?, Error?) -> Void#>)
    
    0 讨论(0)
  • 2021-01-05 16:02

    this code works for me

    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)")
            }
          })
    
    0 讨论(0)
提交回复
热议问题