上传前预览图像。

56

我有一个页面,其中有四张图片供用户选择。我希望用户能够在上传前预览每个图片。

下面的 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);
});

1
也许对你来说使用类似 jQuery 文件上传 插件会更容易些? - Artur Filipiak
1
只是不要使用单个#output图像? - Bergi
一个你建议的例子会很好@bergi - silvercity
10个回答

86

这里是为您提供的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>


@Artem Solovev,我们可以在您上面的解决方案中设置最大图像数量吗? - Billu
3
我该如何在这里添加一个删除功能? - hihihihi
@ArtemSolovev 如何在读取器 onload 中获取 i 值?alert(i) 将得到相同的结果值。 - HiDayurie Dave
1
我只是做了一个小修改,我在我的函数顶部添加了$("div.gallery").html("");,这样每次选择文件时,以前的缩略图就会被删除。 - Muhammad Bilal
如果用户选择同一张图片超过一次,则会再次显示相同的图片。 - Mehdi Dehghani
@MehdiDehghani,任务中没有提到,所以如果你想的话,可以修复它。 - Kas Elvirov

45
  • 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
}

1
你好,非常感谢,它正常工作。我们如何为此添加删除选项? - matheen ulla
1
@matheenulla,从DOM中删除“缩略图/预览”图像元素并不意味着您已将其从分配给该input type="file"元素的FileList中删除 - 因此可能会提交到服务器。我的建议是将整个操作存储起来,然后重新开始。至少这是最简单的方法。请参见上面的扩展示例。 - Roko C. Buljan

6

function previewMultiple(event){
        var saida = document.getElementById("adicionafoto");
        var quantos = saida.files.length;
        for(i = 0; i < quantos; i++){
            var urls = URL.createObjectURL(event.target.files[i]);
            document.getElementById("galeria").innerHTML += '<img src="'+urls+'">';
        }
    }
#galeria{
        display: flex;
    }
    #galeria img{
        width: 85px;
        height: 85px;
        border-radius: 10px;
        box-shadow: 0 0 8px rgba(0,0,0,0.2);
        opacity: 85%;
    }
<input type="file" multiple onchange="previewMultiple(event)" id="adicionafoto">
<div id="galeria">
    
</div>


5

只需使用FileReader.readAsDataURL()

HTML:

<div id='photos-preview'></div>
<input type="file" id="fileupload" multiple (change)="handleFileInput($event.target.files)" />

JS:

 function handleFileInput(fileList: FileList) {
        const preview = document.getElementById('photos-preview');
        Array.from(fileList).forEach((file: File) => {
            const reader = new FileReader();
            reader.onload = () => {
              var image = new Image();
              image.src = String(reader.result);
              preview.appendChild(image);
            }
            reader.readAsDataURL(file);
        });
    }

演示


1
我们如何将其用于多个单独的输入标签,而不是单个带有多个图像预览的输入? - Giorgi Gvimradze
嗨,Giorgi,对于FileList,请使用forEach,并使用新的FileReader在任何文件上进行readAsDataURL。 - J. Jerez

0

$(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>

function previewImages() {

  var preview = document.querySelector('#preview');
  
  if (this.files) {
    [].forEach.call(this.files, readAndPreview);
  }

  function readAndPreview(file) {

    // Make sure `file.name` matches our extensions criteria
    if (!/\.(jpe?g|png|gif)$/i.test(file.name)) {
      return alert(file.name + " is not an image");
    } // else...
    
    var reader = new FileReader();
    
    reader.addEventListener("load", function() {
      var image = new Image();
      image.height = 100;
      image.title  = file.name;
      image.src    = this.result;
      preview.appendChild(image);
    });
    
    reader.readAsDataURL(file);
    
  }

}

document.querySelector('#file-input').addEventListener("change", previewImages);
<input id="file-input" type="file" multiple>
<div id="preview"></div>


你的第二个例子就是对一个现有的答案(现在已经被编辑过了)的完全复制。请避免这种回答行为。 - Roko C. Buljan

0

function previewImages() {

  var preview = document.querySelector('#preview');
  
  if (this.files) {
    [].forEach.call(this.files, readAndPreview);
  }

  function readAndPreview(file) {

    // Make sure `file.name` matches our extensions criteria
    if (!/\.(jpe?g|png|gif)$/i.test(file.name)) {
      return alert(file.name + " is not an image");
    } // else...
    
    var reader = new FileReader();
    
    reader.addEventListener("load", function() {
      var image = new Image();
      image.height = 100;
      image.title  = file.name;
      image.src    = this.result;
      preview.appendChild(image);
    });
    
    reader.readAsDataURL(file);
    
  }

}

document.querySelector('#file-input').addEventListener("change", previewImages);
<input id="file-input" type="file" multiple>
<div id="preview"></div>


我正在使用您的方法来检测类型,但它仍然会弹出警告并添加文件。我该如何避免这种情况?谢谢! - Erick Jones
这是一个现有答案的精确复制现已编辑)。请避免这样的回答行为。 - Roko C. Buljan

0

https://dev59.com/Hm865IYBdhLWcg3wcOLG#59985954

ES2017方式

// convert file to a base64 url
const readURL = file => {
    return new Promise((res, rej) => {
        const reader = new FileReader();
        reader.onload = e => res(e.target.result);
        reader.onerror = e => rej(e);
        reader.readAsDataURL(file);
    });
};

// for demo
const fileInput = document.createElement('input');
fileInput.type = 'file';
const img = document.createElement('img');
img.attributeStyleMap.set('max-width', '320px');
document.body.appendChild(fileInput);
document.body.appendChild(img);

const preview = async event => {
    const file = event.target.files[0];
    const url = await readURL(file);
    img.src = url;
};

fileInput.addEventListener('change', preview);


0
<script type="text/javascript">

var upcontrol = {
  queue : null, // upload queue
  now : 0, // current file being uploaded
  start : function (files) {
  // upcontrol.start() : start upload queue

    // WILL ONLY START IF NO EXISTING UPLOAD QUEUE
    if (upcontrol.queue==null) {
      // VISUAL - DISABLE UPLOAD UNTIL DONE
      upcontrol.queue = files;
      document.getElementById('uploader').classList.add('disabled');
      // PREVIEW UPLOAD IMAGES
       upcontrol.preview();*enter code here*
      //PROCESS UPLOAD ON CLICK 

    $('#add_files').on('click', function() {
       upcontrol.run();
    });
    }
  },
  preview : function() {
    //upcontrol.preview() : preview uploading file
        if (upcontrol.queue) {
            var filesAmount = upcontrol.queue.length;

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

                reader.onload = function(event) {
                  var fimg = document.createElement('img') 
                    fimg.src =  event.target.result,
                      fimg.classList = "col-sm-6 col-md-6 col-lg-4 float-left center",
                      document.getElementById('gallery').appendChild(fimg);                
                }
                reader.readAsDataURL(upcontrol.queue[i]);
            }
        }
    },
  run : function () {
  // upcontrol.run() : proceed upload file

    var xhr = new XMLHttpRequest(),
        data = new FormData();
    data.append('file-upload', upcontrol.queue[upcontrol.now]);
    xhr.open('POST', './lockeroom/func/simple-upload.php', true);
    xhr.onload = function (e) {

      // SHOW UPLOAD STATUS
      var fstat = document.createElement('div'),

          txt = upcontrol.queue[upcontrol.now].name + " - ";
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          // SERVER RESPONSE
          txt += xhr.responseText;
        } else {
          // ERROR
          txt += xhr.statusText;
        }
      }
      fstat.innerHTML = txt;
      document.getElementById('upstat').appendChild(fstat);

      // UPLOAD NEXT FILE
      upcontrol.now++;
      if (upcontrol.now < upcontrol.queue.length) {
        upcontrol.run();
      }
      // ALL DONE
      else {
        upcontrol.now = 0;
        upcontrol.queue = null;
        document.getElementById('uploader').classList.remove('disabled');
      }
    };
    xhr.send(data);
  }
};

window.addEventListener("load", function () {
  // IF DRAG-DROP UPLOAD SUPPORTED
  if (window.File && window.FileReader && window.FileList && window.Blob) {
    /* [THE ELEMENTS] */
    var uploader = document.getElementById('uploader');

    /* [VISUAL - HIGHLIGHT DROP ZONE ON HOVER] */
    uploader.addEventListener("dragenter", function (e) {
      e.preventDefault();
      e.stopPropagation();
      uploader.classList.add('highlight');
    });
    uploader.addEventListener("dragleave", function (e) {
      e.preventDefault();
      e.stopPropagation();
      uploader.classList.remove('highlight');
    });

    /* [UPLOAD MECHANICS] */
    // STOP THE DEFAULT BROWSER ACTION FROM OPENING THE FILE
    uploader.addEventListener("dragover", function (e) {
      e.preventDefault();
      e.stopPropagation();
    });

    // ADD OUR OWN UPLOAD ACTION
    uploader.addEventListener("drop", function (e) {
      e.preventDefault();
      e.stopPropagation();
      uploader.classList.remove('highlight');
      upcontrol.start(e.dataTransfer.files);
    });
  }
  // FALLBACK - HIDE DROP ZONE IF DRAG-DROP UPLOAD NOT SUPPORTED
  else {
    document.getElementById('uploader').style.display = "none";
  }
});
</script>

我使用类似这样的东西,得到了最好的结果并且易于理解。

<p id="uploader" class="">拖拽到此处</p> <div id="gallery" class=""></div> <br> <div id="upstat"></div> <input id="add_files" type="button" class="btn btn-primary" value="添加图片" name="add_files"> 可用于HTML拖拽上传器 - Orgkid

0
function appendRows(){
    $i++;
    var html='';
    html+='<div id="remove'+$i+'"><input type="file" name="imagefile[]" accept="image/*" onchange="appendloadFile('+$i+')"><img id="outputshow'+$i+'" height="70px" width="90px"><i onclick="deleteRows('+$i+')" class="fas fa-trash-alt"></i></div>';
     $("#appendshow").append(html);
}

function appendloadFile(i){
    var appendoutput = document.getElementById('outputshow'+i+'');
    appendoutput.src = URL.createObjectURL(event.target.files[0]);
}

-1

$(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">

$(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">


虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。 - thewaywewere

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