# 文件上传

# 1. 结构

<label>
  <span>选中文件</span>
  <input type="file" style="display: none;" onchange="handleFileInputChange()" />
</label>

说明:

  • 隐藏 <input type="file">
  • <label> 包裹 <input>,点击 <label> 后触发 <input>

# 2. 选择图片转 base64 上传

<button onclick="handleClickButton()">点击上传</button>
<input id="fileInput" type="file" onchange="handleFileInputChange()">

<script>
  const fileInput = document.getElementById("fileInput");

  function handleClickButton() {
    console.log('handleClickButton')
    fileInput.click();
  }

  function handleFileInputChange () {
    const file = fileInput.files[0];

    const reader = new FileReader();

    if (file) {
      reader.readAsDataURL(file);

      reader.onload = function () {
        const base = reader.result;
        console.log(base);
        // data:image/png;base64,xxxx
      }
    }
      
  }
</script>

# 3. axios 文件上传

vue2 版本:

<input id="fileInput" type="file" onchange="handleFileInputChange()">

<script>
  const fileInput = document.getElementById("fileInput");

  function upload() {
    const file = fileInput.files[0];    

    const param = new FormData();       

    // 文件
    param.append('file', file)   

    // 数据
    param.append("name", "张三");
    param.append("age", 18);

    const config = {
      headers: {'Content-Type': 'multipart/form-data'}
    }

    axios.post("uploadLogo", param, config);

  }
</script>

# 4. 校验图片类型和尺寸

<input id="fileInput" type="file" onchange="handleFileInputChange()">

<script>
  const fileInput = document.getElementById("fileInput");

  function handleChange() {
    if (!validateFileExt()) {
      return;
    }

    if (!validateFileSize(64)) {
      return;
    }
  }

  // 后缀名
  function validateFileExt() {
    const acceptExts = ['.jpg', '.png', '.gif', '.jpeg'];
    const fileName = fileInput.value;
    
    // null | ['.jpg']
    const matches = fileName.match(/\.[a-zA-Z0-9]+$/);

    if (!matches) {
      return false;
    }

    const ext = matches[0];

    return acceptExts.includes(ext);
  }

  // 文件尺寸
  function validateFileSize(maxKilobyte) {
    const file = fileInput.files[0];    
    const fileBytes = file.size; // 单位字节

    return fileBytes <= maxKilobyte * 1024;
  }
</script>

# 5. 拖拽选择文件

原生:

<style>
  .input-file { display: none; }

  .file-selector { 
    display: block; margin: 40px; width: 400px; height: 300px; 
    border: dashed 1px #999; }

  .file-selector.hover { border-color: red; }
</style>

<label id="fileInputLabel" for="fileInput" class="file-selector">
  选择文件或拖拽文件
</label>
<input id="fileInput" class="input-file" type="file" accept="image/*">

<script>
  const fileInputElt = document.querySelector('#fileInput');
  const fileInputLabelElt = document.querySelector('#fileInputLabel');

  fileInputLabelElt.addEventListener('dragover', handleDragHover, false);
  fileInputLabelElt.addEventListener('dragleave', handleDragLeave, false);
  fileInputLabelElt.addEventListener('drop', handleSelectFile, false);

  fileInputElt.addEventListener('change', handleSelectFile, false);

  function handleDragHover(e) {
    e.stopPropagation();
    e.preventDefault();
    fileInputLabelElt.classList.add('hover')
  }
  function handleDragLeave(e) {
    e.stopPropagation();
    e.preventDefault();
    fileInputLabelElt.classList.remove('hover')
  }
  function handleSelectFile(e) {
    e.stopPropagation();
    e.preventDefault();

    const files = e.target.files || e.dataTransfer.files;

    console.log(files[0]);
  }

</script>

vue:

<template>
  <div
    @dragover.prevent="handleDragOver"
    @dragleave.prevent="handleDragLeave"
    @drop.prevent="handleDrop"
  >
    ...
  </div>
</template>
<script lang="ts">
export default {
  methods: {
    handleDragOver() {
      this.dndOver = true;
    },
    handleDragLeave() {
      this.dndOver = false;
    },
    handleDrop(event: DragEvent) {
      const { dataTransfer } = event;

      if (!dataTransfer) {
        return;
      }

      const files: File[] = [];

      if (dataTransfer.items) {
        [...dataTransfer.items].forEach((item) => {
          if (item.kind === 'file') {
            const file = item.getAsFile();
            if (file) {
              files.push(file);
            }
          }
        });
      } else {
        [...dataTransfer.files].forEach((file) => {
          files.push(file);
        });
      }

      this.addFiles(files);
    },

    addFiles(files: File[]) {

    },
  }
}
</script>

参考:

# 6. 选择文件时限制类型

示例:

<p> .xls .xlsx :<input type="file" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"  /> </p>

<p> image/* :<input type="file" accept="image/*"  /> </p>

<p> image/png :<input type="file" accept="image/png"  /> </p>

<p> image/gif :<input type="file" accept="image/gif"  /> </p>

<p> image/jpeg :<input type="file" accept="image/jpeg"  /> </p>

参考:

# 7. 参考

本章目录