7
votes

XMLHttpRequest.upload.onprogress ne fonctionne pas avec HTTPS

Problème

J'ai une page sur laquelle les utilisateurs peuvent télécharger des fichiers à l'aide de FormData et d'une XMLHttpRequest . Le téléchargement du fichier fonctionne correctement. Mais upload.onprogress ne fonctionne que lors du téléchargement à partir d'une connexion HTTP .

HTTPS

HTTPS

HTTP

 HTTP

J'ai testé ceci sur Heroku et sur une instance Amazon EC2. Mais c'est toujours la même chose:

  • La progression est affichée lors du téléchargement via HTTP
  • L'événement de progression n'est jamais déclenché lors du téléchargement via HTTPS

Javascript (Angular 7)

xhr.upload.addEventListener("progress", uploadProgress, false);

Node.Js

const busboy = new Busboy({ headers: req.headers })
busboy.on('finish', async () => {

    const fileData = req.files.file
    const fileId = req.body.fileId
    const params = {
        Body: fileData.data,
        Bucket: awsConfig.bucket,
        ContentType: fileData.mimetype,
        Key: fileId,
        StorageClass: 'ONEZONE_IA',
    }
    awsConfig.s3.upload(params, (err, data) => { /* ... */ }

})
req.pipe(busboy)

const busboyBodyParser = require('busboy-body-parser');
app.use(busboyBodyParser())

3 Réponses :


1
votes

En essayant de reproduire ce problème, je n'ai pas rencontré le même problème. Pourriez-vous s'il vous plaît vérifier ci-dessous l'application Heroku simple que j'ai créée pour tester ce problème spécifique? De plus, s'il manque une pièce que je ne vois pas, veuillez m'en informer.

Application de test Heroku : https://erdsav-test-app.herokuapp.com/

Voici le code JS que je J'ai essayé de construire au-dessus de votre code et il télécharge simplement les données zip et les télécharge ensuite (ne peut pas stocker sur Heroku en raison du système de fichiers Ephemeral ) pour garantir que le fichier est téléversé avec succès;

<!DOCTYPE html>
<html lang="en">
    <title>ProgressBar Progress Test</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="description" content="ProgressBar Progress Test">
    <body>          
            <form method="post" enctype="multipart/form-data">
                <input type="file" id="file_upload" accept="application/zip" style="width:0px" />
                <button id="select_button">Choose File to Upload</button>
                <progress id="progress" value="0"></progress>
                <span id="progress_display"></span>
                <button type="submit" id="upload_button">Start Upload Process</button>
            </form>
            <div id="file_details" style="display: none">
                <h3>Selected File Details</h3>
                <span id="file_name"></span><br>
                <span id="file_size"></span>
            </div>
            <script type="module" src="js/Observable.js"></script>  
            <script src="js/download.js"></script>  
            <script type="module" src="js/main.js"></script>                                
    </body>
</html>

Côté PHP

<?php

if ($_FILES['file']['error'] != $UPLOAD_ERR_OK) {
    //writeLog($_FILES['file']['error']);
    echo 'An error occurred!'; 
    exit();
} 
else { 
   $filePath = $_FILES['file']['tmp_name'];
   $fileName = $_FILES['file']['name']; 

   if (file_exists($filePath)) {
        ob_start();
        $fileSize = readfile($filePath);
        $content = ob_get_clean();

        header('Content-Type: application/octet-stream;');
        header("Content-Disposition: attachment; filename=\"" . $fileName . "\"");
        header('Expires: 0');
        header('Pragma: no cache');
        header('Content-Length: ' . $fileSize);

        echo $content;
   }
   else{
       echo 'File is not found';
       exit();
   }
}

?>

Source de la page HTML

import { Observable } from "../js/Observable.js";

document.addEventListener("DOMContentLoaded", function(event) {
    var progressBar = document.getElementById("progress"),
    fileNameSpan = document.getElementById("file_name"),
    fileSizeSpan = document.getElementById("file_size"),
    fileUploadComp = document.getElementById("file_upload"),
    loadButton = document.getElementById("upload_button"),
    displaySpan = document.getElementById("progress_display"),
    fileDetails = document.getElementById("file_details"),
    selectButton = document.getElementById("select_button"),
    formData = null;

    function hideElements(){
        fileDetails.style.display = "none";
    }

    function showElements(){
        fileDetails.style.display = "block";
    }

    function upload(payload, fileName){
        return new Observable(observer => {
            const xhr = new XMLHttpRequest();
            let progress = 0;

            /** THIS EVENT IS NOT WORKING WITH HTTPS */
            xhr.upload.onprogress = (event => {
                if (event.lengthComputable) {
                    progressBar.max = event.total;
                    progressBar.value = event.loaded;
                    progress = Math.floor((event.loaded / event.total) * 100);
                    displaySpan.innerText = progress + '%';
                    observer.next(progress);
                }
            });
            xhr.upload.onloadstart = function(e) {
              progressBar.value = 0;
              displaySpan.innerText = '0%';
            }
            xhr.upload.onloadend = function(e) {
              progressBar.value = e.loaded;
              loadButton.disabled = false;
              loadButton.innerHTML = 'Start Upload Process';
            }

            xhr.responseType = 'blob';
            xhr.open('POST', "https://erdsav-test-app.herokuapp.com/upload.php", true);  
            xhr.send(payload);
            xhr.returnedFileName = fileName;
            xhr.onload = () => {
                download(xhr.response, xhr.returnedFileName, "application/zip");
                observer.next(100);
                observer.complete();
            };
        });
    }

    function showUploadedFile(file){
        var fileName = file.name;
        var fileSize = file.size;

        fileNameSpan.innerText = fileName;
        fileSizeSpan.innerText = Math.floor(fileSize / 1000) + ' KB';
    }

    function buildFormData(file) {      
        if (formData) { 
            formData.append("file", file);
        }     

        return formData;  
    }

    hideElements(); 
    if (window.FormData) {
        formData = new FormData();
    }
    else{
        alert("FormData is not supported in this browser!");
    }

    fileUploadComp.onchange = function(){
        var file = fileUploadComp.files[0];

        if(file){
            showElements();
            showUploadedFile(file);
        }
        else{
            hideElements();
        }
    }

    selectButton.addEventListener("click", function(e){
       fileUploadComp.value = ""; 
       hideElements();    

       fileUploadComp.click();

       e.preventDefault(); 
    });

    loadButton.addEventListener("click", function(e) {
       if(fileUploadComp.files !== undefined && fileUploadComp.files.length > 0){
           this.disabled = true;
           this.innerHTML = "Uploading. Please wait...";

           var obs = upload(buildFormData(fileUploadComp.files[0]), fileUploadComp.files[0].name);
           obs.subscribe(
            function valueHandler(value){
              console.log("UPLOADING");
              if(value){
                  console.log(value);
              }
            },
            function errorHandler(err){
              console.log("THERE IS AN ERROR");
            },
            function completeHandler(){
              console.log("COMPLETE");
            }
            );
        }
        else{
            alert("No file is selected");
        }

        e.preventDefault();
    });
});

L'image ci-dessous est capturée en ralentissant le réseau pour voir le pourcentage actuel pendant que le processus de téléchargement se poursuit;

 Test de téléchargement de fichier

Navigateurs utilisés lors des tests;

Firefox Developer Edition 67.0b 13 (64 bits / à jour)
Google Chrome 74.0.3729.108 (64 bits / à jour)


6 commentaires

Votre application heroku fonctionne aussi pour moi. La seule différence que je peux voir, c'est que j'utilise Node.Js au lieu de PHP. Donc, j'ai ajouté des liens vers mon code source - peut-être que vous pouvez repérer quelque chose qui pourrait causer le problème :)


Salut Florian, merci pour ta réponse. J'ai un peu étudié votre code, pas en détail pour l'instant mais quelque chose a attiré mon attention. Il n'y a pas d'observateur.next (progression); ligne dans votre événement xhr.upload.onprogress. Pouvez-vous vérifier là-bas d'abord?


Je l'ai vérifié et le problème persiste. (Le xhr.upload.onprogress n'est appelé qu'une seule fois)


D'accord, pouvez-vous réessayer en limitant la connexion réseau? (ex. 3G lente activée) Peut-être que la progression se termine si vite et que la progression est appelée une fois? Je demande cela pour m'assurer que la méthode n'est appelée qu'une seule fois.


Il n'est appelé qu'une seule fois, également avec la limitation du réseau


N'essayez pas de vous ennuyer, mais avez-vous trouvé quelque chose qui pourrait potentiellement en être la cause? 😉



0
votes

Je fais exactement la même chose avec l'une de mes webapps mais sans aucune angulaire, juste JS et PHP. Mon xhr fonctionne comme un charme et ressemble à ceci:

handleProgress = function(event){
    var progress = totalProgress + event.loaded;
    document.getElementById('progress').innerHTML = 'Aktueller Fortschritt: ' + ((progress - totalSize < 0) ? Math.floor(progress / totalSize * 10000) / 100 : 100)  + '%';
}

et ceci ma méthode handleProgess:

var totalSize = 0;
var xhr = new XMLHttpRequest();    // den AJAX Request anlegen
xhr.open('POST', 'data/upload.php');    // Angeben der URL und des Requesttyps
xhr.upload.addEventListener("progress", handleProgress);
xhr.addEventListener("load", handleComplete);


0 commentaires

0
votes

Il est important de définir Listener entre:

xhr.open('POST'...);

...put you listener here....

xhr.send(data)

Dans ce cas, cela fonctionnera!


0 commentaires