JavaScript - 文件上传;检查图像是否具有透明背景

11

我想检查图像的透明度,如果图像背景不透明,则显示错误消息。我有一个function hasAlpha(file)用于检查文件是否具有透明背景,但我不确定如何将其通过已经检查文件大小的function uploadFile(file)函数。

我认为我可以将文件大小if语句复制到if (xhr.upload) {}内部,并检查上传的图像是否透明,但我不确定如何将function hasAlpha(file)与其他代码结合起来。

// 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


为什么不将其设置为请求头或查询字符串? - Mr Zach
我不确定如何做。你能否用一小段代码示范一下? - Kyle Underhill
你应该知道,如果你理解你发布的代码。 - Mr Zach
1个回答

11

我修改/修复了您的hasAlpha()函数。它将返回一个Promise,在图像加载事件上解决或在图像错误事件上拒绝。我还添加了隐藏的<canvas>元素,该元素由此函数使用。我在fileSelectHandler()函数中添加了透明度检查,如果需要,可以将其移动到另一个位置。

代码:

// 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>


canvas 允许使用 SVG 吗?我收到了以下错误信息:“CanvasRenderingContext2D 上的 'getImageData':画布已被跨域数据污染。” - Kyle Underhill
如 https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image 所述,添加 img.crossOrigin = "Anonymous" 应该可以解决你提到的问题。我已经将小写的 'anonymous' 改为大写。请现在检查我的片段。 - Andriy
它似乎不接受SVG。我在这里创建了一个CodePen作为参考:https://codepen.io/moofawsaw/pen/RwNVvXV - Kyle Underhill
我已经测试了您的Codepen代码和SVG文件,它似乎可以正常工作,并在控制台中记录了上传的SVG文件是透明的。如果可能的话,请附上您的SVG文件,我会检查一下。 - Andriy
我有几个关于这个脚本的问题:(1)当一个图片被拖到表单上时,它是如何保存文件的?因为对于被拖到表单上的文件,$_FILES["fileUpload"]["name"]; 似乎是空的。如果你点击上传按钮,$_FILES["fileUpload"]["name"]; 会显示文件名。(2)这个脚本将文件保存在哪里? - Dion
目前被拖动的文件还没有保存在任何地方,我只回答了 alpha 透明度检查的部分。负责上传的函数是 uploadFile(f),它只是执行一个 XHR POST 到表单的 action 属性。在给定的示例中,该属性不存在,因此 POST 将转到其默认位置 - 当前位置。我没有处理这部分代码,但是你可以向表单添加一个 action 属性并对其进行检查。感谢您的提问! - Andriy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接