Javascript - przesyłanie plików; sprawdź, czy obraz ma przezroczyste tło

10

Chcę sprawdzić przezroczystość obrazu i wyświetlić komunikat o błędzie, jeśli tło obrazu nie jest przezroczyste. Muszę function hasAlpha(file)to sprawdzić, czy plik ma przezroczyste tło, ale nie jestem pewien, jak przekazać go przez function uploadFile(file)funkcję, która już sprawdza rozmiar pliku.

Myślę, że mogę zduplikować instrukcję rozmiaru pliku w ifśrodku if (xhr.upload) {}i sprawdzić, czy przesłany obraz jest przezroczysty, ale nie jestem pewien, jak włączyć go function hasAlpha(file)z resztą kodu.

// File Upload
//
function ekUpload() {
  function Init() {
    console.log("Upload Initialised");

    var fileSelect = document.getElementById("file-upload"),
      fileDrag = document.getElementById("file-drag"),
      submitButton = document.getElementById("submit-button");

    fileSelect.addEventListener("change", fileSelectHandler, false);

    // Is XHR2 available?
    var xhr = new XMLHttpRequest();
    if (xhr.upload) {
      // File Drop
      fileDrag.addEventListener("dragover", fileDragHover, false);
      fileDrag.addEventListener("dragleave", fileDragHover, false);
      fileDrag.addEventListener("drop", fileSelectHandler, false);
    }
  }

  function fileDragHover(e) {
    var fileDrag = document.getElementById("file-drag");

    e.stopPropagation();
    e.preventDefault();

    fileDrag.className =
      e.type === "dragover" ? "hover" : "modal-body file-upload";
  }

  function fileSelectHandler(e) {
    // Fetch FileList object
    var files = e.target.files || e.dataTransfer.files;

    // Cancel event and hover styling
    fileDragHover(e);

    // Process all File objects
    for (var i = 0, f;
      (f = files[i]); i++) {
      parseFile(f);
      uploadFile(f);
    }
  }

  // Output
  function output(msg) {
    // Response
    var m = document.getElementById("messages");
    m.innerHTML = msg;
  }

  function hasAlpha(file) {
    var canvas = file.getElementById("file-image");
    var ctx = canvas.getContext("2d");
    var data = file.getImageData(0, 0, canvas.width, canvas.height).data,
      hasAlphaPixels = false;
    for (var i = 3, n = data.length; i < n; i += 4) {
      if (data[i] < 255) {
        hasAlphaPixels = true;
        break;
      }
    }
    return hasAlphaPixels;
  }

  function parseFile(file) {
    console.log(file.name);
    output("<strong>" + encodeURI(file.name) + "</strong>");

    // var fileType = file.type;
    // console.log(fileType);
    var imageName = file.name;

    var isGood = /\.(?=svg|jpg|png|jpeg)/gi.test(imageName);
    if (isGood) {
      document.getElementById("start").classList.add("hidden");
      document.getElementById("response").classList.remove("hidden");
      document.getElementById("notimage").classList.add("hidden");
      // Thumbnail Preview
      document.getElementById("file-image").classList.remove("hidden");
      document.getElementById("file-image").src = URL.createObjectURL(file);
    } else {
      document.getElementById("file-image").classList.add("hidden");
      document.getElementById("notimage").classList.remove("hidden");
      document.getElementById("start").classList.remove("hidden");
      document.getElementById("response").classList.add("hidden");
      document.getElementById("file-upload-form").reset();
    }
  }

  function uploadFile(file) {
    var xhr = new XMLHttpRequest(),
      fileInput = document.getElementById("class-roster-file"),
      fileSizeLimit = 1024; // In MB

    if (xhr.upload) {
      // Check if file is less than x MB
      if (file.size <= fileSizeLimit * 1024 * 1024) {
        // File received / failed
        xhr.onreadystatechange = function(e) {
          if (xhr.readyState == 4) {
            // Everything is good!
            // document.location.reload(true);
          }
        };

        // Start upload
        xhr.open(
          "POST",
          document.getElementById("file-upload-form").action,
          true
        );
        xhr.setRequestHeader("X-File-Name", file.name);
        xhr.setRequestHeader("X-File-Size", file.size);
        xhr.setRequestHeader("Content-Type", "multipart/form-data");
        xhr.send(file);
      } else {
        output("Please upload a smaller file (< " + fileSizeLimit + " MB).");
      }
    }
  }

  // Check for the various File API support.
  if (window.File && window.FileList && window.FileReader) {
    Init();
  } else {
    document.getElementById("file-drag").style.display = "none";
  }
}
ekUpload();
form {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: left;
  border: 1px solid;
  cursor: pointer;
  height: 90px;
}

img {
  border: 1px solid;
  height: 60px;
  width: 60px;
}

.uploader input[type="file"],
.hidden {
  display: none;
}
<!-- Upload  -->
<form id="file-upload-form" class="uploader">
  <input id="file-upload" type="file" name="fileUpload" accept="image/*" />
  <label for="file-upload" id="file-drag">
    <img id="file-image" src="#" alt="Preview" class="hidden">
    <div id="start">
      <i class="fa fa-download" aria-hidden="true"></i>
      <div>Select a file or drag here</div>
      <div id="notimage" class="hidden">Please select an image</div>
    </div>
    <div id="response" class="hidden">
      <div id="messages"></div>
    </div>
  </label>
</form>
<div class="error hidden">Your logo must have a transparent background. Please set RBGa

Kyle Underhill
źródło
Dlaczego nie ustawisz go jako nagłówka żądania lub kwerendy?
Pan Zach
Nie jestem pewien jak to zrobić. Czy byłbyś w stanie zademonstrować we fragmencie?
Kyle Underhill
Powinieneś wiedzieć, że jeśli rozumiesz opublikowany kod.
Pan Zach

Odpowiedzi:

9

Zmodyfikowałem / naprawiłem twoją hasAlpha()funkcję. Zwraca obietnicę, która rozwiązuje się przy zdarzeniu ładowania obrazu lub odrzuca przy zdarzeniu błędu obrazu. Dodałem również ukryty <canvas>element, który jest używany przez tę funkcję. Dodałem kontrolę przezroczystości do fileSelectHandler()funkcji, w razie potrzeby przenieś ją w inne miejsce.

Kod:

// File Upload
//
function ekUpload() {
  function Init() {
    console.log("Upload Initialised");

    var fileSelect = document.getElementById("file-upload"),
      fileDrag = document.getElementById("file-drag"),
      submitButton = document.getElementById("submit-button");

    fileSelect.addEventListener("change", fileSelectHandler, false);

    // Is XHR2 available?
    var xhr = new XMLHttpRequest();
    if (xhr.upload) {
      // File Drop
      fileDrag.addEventListener("dragover", fileDragHover, false);
      fileDrag.addEventListener("dragleave", fileDragHover, false);
      fileDrag.addEventListener("drop", fileSelectHandler, false);
    }
  }

  function fileDragHover(e) {
    var fileDrag = document.getElementById("file-drag");

    e.stopPropagation();
    e.preventDefault();

    fileDrag.className =
      e.type === "dragover" ? "hover" : "modal-body file-upload";
  }

  async function fileSelectHandler(e) {
    // Fetch FileList object
    var files = e.target.files || e.dataTransfer.files;

    // Cancel event and hover styling
    fileDragHover(e);

    // Process all File objects
    for (let i = 0; i < files.length; i++) {
      const f = files[i];
      if (await hasAlpha(f)) {
        console.log('Selected image is transparent');
        parseFile(f);
        uploadFile(f);
      }
      else {
        console.log('Selected image is not transparent');
        document.querySelector('#response').classList.remove('hidden');
        document.querySelector('#file-image').classList.add('hidden');
        output('<strong class="warning">Image background is not transparent</strong>');
      }
    }
  }

  // Output
  function output(msg) {
    // Response
    var m = document.getElementById("messages");
    m.innerHTML = msg;
  }

  function hasAlpha(file) {
    return new Promise((resolve, reject) => {
      let hasAlpha = false;
      const canvas = document.querySelector('canvas');
      const ctx = canvas.getContext('2d');
    
      const img = new Image();
      img.crossOrigin = 'Anonymous';
      img.onerror = reject;
      img.onload = function() {
        canvas.width = img.width;
        canvas.height = img.height;
      
        ctx.drawImage(img, 0, 0);
        const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
      
        for (let j = 0; j < imgData.length; j += 4) {
          if (imgData[j + 3] < 255) {
            hasAlpha = true;
            break;
          }
        }
        resolve(hasAlpha);
      };
      img.src = URL.createObjectURL(file);
    });
  }

  function parseFile(file) {
    console.log(file.name);
    output("<strong>" + encodeURI(file.name) + "</strong>");

    // var fileType = file.type;
    // console.log(fileType);
    var imageName = file.name;

    var isGood = /\.(?=svg|jpg|png|jpeg)/gi.test(imageName);
    if (isGood) {
      document.getElementById("start").classList.add("hidden");
      document.getElementById("response").classList.remove("hidden");
      document.getElementById("notimage").classList.add("hidden");
      // Thumbnail Preview
      document.getElementById("file-image").classList.remove("hidden");
      document.getElementById("file-image").src = URL.createObjectURL(file);
    } else {
      document.getElementById("file-image").classList.add("hidden");
      document.getElementById("notimage").classList.remove("hidden");
      document.getElementById("start").classList.remove("hidden");
      document.getElementById("response").classList.add("hidden");
      document.getElementById("file-upload-form").reset();
    }
  }

  function uploadFile(file) {
    var xhr = new XMLHttpRequest(),
      fileInput = document.getElementById("class-roster-file"),
      fileSizeLimit = 1024; // In MB

    if (xhr.upload) {
      // Check if file is less than x MB
      if (file.size <= fileSizeLimit * 1024 * 1024) {
        // File received / failed
        xhr.onreadystatechange = function(e) {
          if (xhr.readyState == 4) {
            // Everything is good!
            // document.location.reload(true);
          }
        };

        // Start upload
        xhr.open(
          "POST",
          document.getElementById("file-upload-form").action,
          true
        );
        xhr.setRequestHeader("X-File-Name", file.name);
        xhr.setRequestHeader("X-File-Size", file.size);
        xhr.setRequestHeader("Content-Type", "multipart/form-data");
        xhr.send(file);
      } else {
        output("Please upload a smaller file (< " + fileSizeLimit + " MB).");
      }
    }
  }

  // Check for the various File API support.
  if (window.File && window.FileList && window.FileReader) {
    Init();
  } else {
    document.getElementById("file-drag").style.display = "none";
  }
}
ekUpload();
form {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: left;
  border: 1px solid;
  cursor: pointer;
  height: 90px;
}

img {
  border: 1px solid;
  height: 60px;
  width: 60px;
}

.uploader input[type="file"],
.hidden {
  display: none;
}

.warning {
  color: red;
  font-weight: bold;
}
canvas {
  position: absolute;
  top: -2000px;
}
<!-- Upload  -->
<form id="file-upload-form" class="uploader">
  <input id="file-upload" type="file" name="fileUpload" accept="image/*" />
  <label for="file-upload" id="file-drag">
    <img id="file-image" src="#" alt="Preview" class="hidden">
    <div id="start">
      <i class="fa fa-download" aria-hidden="true"></i>
      <div>Select a file or drag here</div>
      <div id="notimage" class="hidden">Please select an image</div>
    </div>
    <div id="response" class="hidden">
      <div id="messages"></div>
    </div>
  </label>
</form>
<div class="error hidden">Your logo must have a transparent background. Please set RBGa</div>
<canvas></canvas>

Andrij
źródło
Czy canvaspozwala na SVG? Otrzymuję następujący błąd „getImageData” w „CanvasRenderingContext2D”: płótno zostało skażone danymi pochodzącymi z różnych źródeł. ”
Kyle Underhill
jak wspomniano na developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image , dodawanie img..crossOrigin = "Anonymous"powinno rozwiązać wspomniany przez ciebie problem. Naprawiłem to z małych „anonimowych” na wielkie litery. Sprawdź teraz mój fragment
Andriy
Wydaje się, że nie akceptuje svg. Stworzyłem tutaj codepen w celach informacyjnych: codepen.io/moofawsaw/pen/RwNVvXV
Kyle Underhill
Przetestowałem twój kod codepen z plikiem SVG i wydaje się, że działa, zalogował się również w konsoli, że przesłany plik SVG jest przezroczysty. Załącz plik SVG (jeśli to możliwe), a ja to sprawdzę
Andriy
Mam kilka pytań na temat tego skryptu: (1) przeciągnięcie obrazu do formularza, w jaki sposób zapisuje plik, ponieważ $ _FILES ["fileUpload"] ["name"]; wydaje się być pusty dla plików przeciągniętych do formularza. Jeśli klikniesz przycisk przesyłania $ _FILES [„fileUpload”] [„name”]; pokazuje plik. (2) Gdzie ten skrypt zapisuje pliki?
Dion