上传前预览图像

新手上路,请多包涵

我有一个包含四个图像的页面供用户选择。我希望用户能够在上传之前预览网站上的每张图片。

下面的 JavaScript 代码仅适用于一张图片,但我希望它适用于通过 <input type="file"> 上传的多张图片。

最好的方法是什么?

 function readURL(input) {
    if (input.files && input.files[0]) {
        var reader = new FileReader();

        reader.onload = function (e) {
            $('#output').attr('src', e.target.result);
        }

        reader.readAsDataURL(input.files[0]);
    }
}

$("#file-input").change(function () {
    readURL(this);
});

原文由 silvercity 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 417
2 个回答

这是适合您的 jQuery 版本。我认为这是更简单的事情。

 $(function() {
    // Multiple images preview in browser
    var imagesPreview = function(input, placeToInsertImagePreview) {

        if (input.files) {
            var filesAmount = input.files.length;

            for (i = 0; i < filesAmount; i++) {
                var reader = new FileReader();

                reader.onload = function(event) {
                    $($.parseHTML('<img>')).attr('src', event.target.result).appendTo(placeToInsertImagePreview);
                }

                reader.readAsDataURL(input.files[i]);
            }
        }

    };

    $('#gallery-photo-add').on('change', function() {
        imagesPreview(this, 'div.gallery');
    });
});
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<input type="file" multiple id="gallery-photo-add">
<div class="gallery"></div>

原文由 Kas Elvirov 发布,翻译遵循 CC BY-SA 3.0 许可协议

  • multiple 属性 添加到您的 HTMLInputElement
  • accept 属性 添加到您的 HTMLInputElement

仅将文件选择过滤为图像,请使用 accept="image/*" 或逗号分隔的 MIME 列表: accept="image/png, image/jpeg" - 使用 FileReader.readAsDataURL 获取 Base64 字符串,

URL.createObjectURL 获取文件 Blob 对象


使用 FileReader.readAsDataURL

读取图像数据的 异步 方式是使用 FileReader API 及其 readAsDataURL 返回 Base64 字符串的方法:

 const preview = (file) => {
  const fr = new FileReader();
  fr.onload = () => {
    const img = document.createElement("img");
    img.src = fr.result;  // String Base64
    img.alt = file.name;
    document.querySelector('#preview').append(img);
  };
  fr.readAsDataURL(file);
};

document.querySelector("#files").addEventListener("change", (ev) => {
  if (!ev.target.files) return; // Do nothing.
  [...ev.target.files].forEach(preview);
});
 #preview img { max-height: 100px; }
 <input id="files" type="file" accept="image/*" multiple>
<div id="preview"></div>

异步策略:

由于 FileReader 的异步特性,您可以实施 async/await _策略_:

 // DOM utility functions:

const el = (sel, par) => (par || document).querySelector(sel);
const elNew = (tag, props) => Object.assign(document.createElement(tag), props);

// Preview images before upload:

const elFiles = el("#files");
const elPreview = el("#preview");

const previewImage = (props) => elPreview.append(elNew("img", props));

const reader = (file, method = "readAsDataURL") => new Promise((resolve, reject) => {
  const fr = new FileReader();
  fr.onload = () => resolve({ file, result: fr.result });
  fr.onerror = (err) => reject(err);
  fr[method](file);
});

const previewImages = async(files) => {
  // Remove existing preview images
  elPreview.innerHTML = "";

  let filesData = [];

  try {
    // Read all files. Return Array of Promises
    const readerPromises = files.map((file) => reader(file));
    filesData = await Promise.all(readerPromises);
  } catch (err) {
    // Notify the user that something went wrong.
    elPreview.textContent = "An error occurred while loading images. Try again.";
    // In this specific case Promise.all() might be preferred over
    // Promise.allSettled(), since it isn't trivial to modify a FileList
    // to a subset of files of what the user initially selected.
    // Therefore, let's just stash the entire operation.
    console.error(err);
    return; // Exit function here.
  }

  // All OK. Preview images:
  filesData.forEach(data => {
    previewImage({
      src: data.result, // Base64 String
      alt: data.file.name // File.name String
    });
  });
};

elFiles.addEventListener("change", (ev) => {
  if (!ev.currentTarget.files) return; // Do nothing.
  previewImages([...ev.currentTarget.files]);
});
 #preview img { max-height: 100px; }
 <input id="files" type="file" accept="image/*" multiple>
<div id="preview"></div>

使用 URL.createObjectURL

读取图像的 同步 方式是使用 URL API 及其 createObjectURL 返回 Blob 的方法:

 const preview = (file) => {
  const img = document.createElement("img");
  img.src = URL.createObjectURL(file);  // Object Blob
  img.alt = file.name;
  document.querySelector('#preview').append(img);
};

document.querySelector("#files").addEventListener("change", (ev) => {
  if (!ev.target.files) return; // Do nothing.
  [...ev.target.files].forEach(preview);
});
 #preview img { max-height: 120px; }
 <input id="files" type="file" accept="image/*" multiple>
<div id="preview"></div>

虽然看起来简单得多,但由于其同步性,它会对主线程产生影响,并且需要您手动使用(如果可能) URL.revokeObjectURL 以释放内存:

 // Remove unused images from #preview? Consider also using
URL.revokeObjectURL(someImg.src); // Free up memory space

jQuery 示例:

以上的 jQuery 实现 FileReader.readAsDataURL() 示例:

 const preview = (file) => {
  const fr = new FileReader();
  fr.onload = (ev) => {
    $('#preview').append($("<img>", {src: fr.result, alt: file.name}));
  };
  fr.readAsDataURL(file);
};

$("#files").on("change", (ev) => {
  if (!ev.target.files) return; // Do nothing.
  [...ev.target.files].forEach(preview);
});
 #preview img { max-height: 120px; }
 <input id="files" type="file" accept="image/*" multiple>
<div id="preview"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>

补充阅读:

尖端:

除了使用 HTMLInputElement 属性 accept ,如果您想在 JavaScript 中确保文件 _属于类型_,您可以:

 if (!/\.(jpe?g|png|gif)$/i.test(file.name)) {
   // Not a valid image
}

或者喜欢:

 if (!/^image\//i.test(file.type)) {
   // File is not of type Image
}

原文由 Roko C. Buljan 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题