Pourquoi est-fs.createReadStream ... pipe (res) verrouiller le fichier lu?

j'utilise express pour streamer des fichiers audio et vidéo selon cette réponse . Le code correspondant ressemble à ceci:

function streamMedia(filePath, req, res) {
  // code here to determine which bytes to send, compute response headers, etc.

  res.writeHead(status, headers);
  var stream = fs.createReadStream(filePath, { start, end })
    .on('open', function() {
      stream.pipe(res);
    })
    .on('error', function(err) {
      res.end(err);
    })
  ;
}

cela fonctionne très bien pour transférer des octets vers les éléments <audio> et <video> sur le client. Cependant, après que ces requêtes ont été servies, une autre requête express peut supprimer le fichier en cours de lecture du système de fichiers. Cette seconde requête échoue, en quelque sorte.

ce qui se passe est que tant que le fichier est redirigé au moins une fois (ce qui signifie qu'un createReadStream a été invoqué pour le chemin du fichier lors de l'exécution du code ci-dessus), alors une autre requête express arrive pour supprimer le fichier, le fichier reste sur le système de fichiers jusqu'à ce qu'express soit arrêté. Dès qu'express est arrêté, les fichiers sont supprimés du système de fichiers.

que se passe-t-il exactement? Est-il fs ou express qui verrouille le fichier, pourquoi, et comment puis-je obtenir le processus de libérer le fichier de sorte qu'il puisse être supprimé (après que son contenu a été lu et transmis à une réponse, s'il y en a en attente)?

Maj 1:

j'ai modifié le code ci-dessus pour mettre autoClose: true pour la seconde fonction arg, et j'ai ajouté à la fois 'end' et 'close' event handlers, comme suit:

res.writeHead(status, headers);
var streamReadOpts = { start: start, end: end, autoClose: true };
var stream = fs.createReadStream(filePath, streamReadOpts)
    // previous 'open' & 'error' event handlers are still here
    .on('end', function () {
      console.log('stream end');
    })
    .on('close', function () {
      console.log('stream close');
    })

ce que j'ai découvert est que lorsqu'une page se charge initialement avec un élément <video> ou <audio> , seulement le 'open' est même tiré. Ensuite, lorsque l'utilisateur clique pour lire la vidéo/audio, une deuxième demande est faite, et cette deuxième fois, les deux événements 'end' et 'close' feu, et ensuite la suppression du fichier succède.

ainsi il apparaît que le fichier est verrouillé quand un utilisateur charge la page qui a l'élément <video> ou <audio> qui obtient son source de la requête qui appelle cette fonction. Ce n'est que lorsque ce fichier multimédia est lu qu'une deuxième demande est faite, et le fichier est déverrouillé.

j'ai également découvert que la fermeture du navigateur provoque également le déclenchement des événements 'end' et 'close' , et que le fichier est déverrouillé. Je pense que je fais quelque chose de mal avec l'express res pour ne pas le fermer correctement, mais je ne suis toujours pas sûr de ce que cela pourrait être.

3
demandé sur Community 2016-08-20 05:31:22

2 réponses

il s'est avéré que la solution à cela était de lire et Piper des blocs plus petits de données du dossier pendant chaque demande. Dans Mes cas de test pour ceci, je diffusais un fichier vidéo MP4 de 6MB. Bien que j'aie pu reproduire le problème en utilisant soit firefox soit chrome, j'ai débogué en utilisant ce dernier, et j'ai trouvé que le client bloquait le flux .

lorsque la page se charge initialement, il y a un élément qui ressemble à quelque chose comme ceci:

<video> <!-- or <audio> -->
    <source src="/path/to/express/request" type="video/mpeg" /> <!-- or audio/mpeg -->
</video> <!-- or </audio> -->

comme cela est documenté dans l'autre réponse référencée dans L'OP, chrome enverra une demande avec un en-tête de gamme comme ceci:

Range:bytes=0-

pour cette requête, ma fonction envoyait le fichier entier, et ma réponse ressemblait à ceci:

Accept-Ranges:bytes
Connection:keep-alive
Content-Length:6070289
Content-Range:bytes 0-6070288/6070289
Content-Type:video/mp4

cependant, chrome ne lisait pas tout le flux . Il ne lisait que la première 3-4MB, puis bloquait la connexion jusqu'à ce qu'une action de l'utilisateur l'amène à besoin le reste du fichier. Cela explique pourquoi la fermeture du navigateur ou l'arrêt d'express ont fait déverrouiller les fichiers, car cela a fermé la connexion du navigateur ou de la fin du serveur.

ma solution actuelle est d'envoyer seulement un maximum de 1MB (la vieille école 1MB, 1024 * 1024 ) morceau à la fois. Le code correspondant se trouve dans , une réponse supplémentaire à la question référencée dans le PO .

2
répondu danludwig 2017-05-23 11:53:03

Set autoClose = true in options. Si autoClose = false Vous devez le fermer manuellement dans l'événement 'end'.

Refer node doc: - https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options

1
répondu Abhilash Km 2016-08-20 02:51:13