【進(jìn)階】文件上傳

導(dǎo)航:
FormData
XMLHttpRequest
上傳前預(yù)覽圖片和文件
上傳進(jìn)度信息
下載進(jìn)度信息
<input type='file'>
new Image()
最后: 完整示例

(1) FormData()對(duì)象

提供一種表示表單數(shù)據(jù)的鍵值對(duì)構(gòu)造方式,即用鍵值對(duì)來模擬一系列表單控件
( 即: 把form中所有表單元素的 name 和 value 組裝成一個(gè)queryString )

  • 特點(diǎn):相對(duì)于普通的ajax,使用FormData最大的優(yōu)點(diǎn)是可以 - 異步上傳二進(jìn)制文件
  • 如果表單的 enctype 屬性設(shè)置為 multipart/form-data,則會(huì)使用表單的submit()方法來發(fā)送數(shù)據(jù),即會(huì)使用和表單一樣的格式
  • 可以使用XMLHttpRequest的 send() 方法來異步的提交這個(gè)"表單"

FormData對(duì)象如何使用?

  1. FormData對(duì)象的方法都在原型鏈條上,自身沒有任何屬性和方法


    FormData對(duì)象的操作方法都在原型鏈上
const formData = new FormData()  ----------- FormData構(gòu)造函數(shù)生成一個(gè)新的 FormData 對(duì)象

formData.append('name', 'wang')  ----------- 添加數(shù)據(jù)
//通過append(key, value)來添加數(shù)據(jù),如果 key不存在則會(huì)新增一條數(shù)據(jù),如果key存在,則添加到數(shù)據(jù)的末尾

formData.get('name')  ---------------------- 獲取數(shù)據(jù),獲取key=name的第一個(gè)值
formData.getAll('name') -------------------- 獲取key=name的所有值

formData.set('name', 'zhang') -------------- 修改數(shù)據(jù)

formData.has('name') ----------------------- 是否存在

formData.delete('name') -------------------- 刪除數(shù)據(jù)

https://segmentfault.com/a/1190000006716454
https://blog.csdn.net/zqian1994/article/details/79635413
https://segmentfault.com/a/1190000012327982
https://segmentfault.com/a/1190000004664783

(2) XMLHttpRequest

XMLHttpRequest對(duì)象來發(fā)送一個(gè)Ajax請(qǐng)求,
XMLHttpRequest()構(gòu)造函數(shù)初始化一個(gè) XMLHttpRequest 對(duì)象,必須在所有其他方法被調(diào)用前調(diào)用構(gòu)造函數(shù)。

  • 如何獲取response?xhr.responsexhr.responseTextxhr.responseXML
const api = new XMLHttpRequest();


方法:
(1) api.open() -------- 初始化HTTP請(qǐng)求參數(shù)(url, http方法等),但并不發(fā)送請(qǐng)求!!! 供 send() 方法使用

api.open(method, url, async, username, password)
method參數(shù):是http請(qǐng)求的方法,值包括:GET,POST,HEAD
url參數(shù):請(qǐng)求的地址
async參數(shù):是否異步,默認(rèn)是true,即異步的發(fā)送請(qǐng)求。
           false,同步,對(duì)send()方法的調(diào)用將阻塞,直到響應(yīng)完全接收
           true或者省略,異步,且通常需要調(diào)用 onreadystatechange() 方法



(2) api.send() -------- 發(fā)送一個(gè)http請(qǐng)求,請(qǐng)求參數(shù)寫在send()方法的參數(shù)中

api.send(body)
get請(qǐng)求:參數(shù)可以直接寫在open()方法中
post請(qǐng)求:參數(shù)寫在該方法中,即body
-- 注意:body參數(shù)的數(shù)據(jù)類型會(huì)影響requestHeader中的 Content-Type 的默認(rèn)值,如何手動(dòng)指定則會(huì)覆蓋默認(rèn)值
如果data是 Document 類型,同時(shí)也是HTML Document類型,
  則content-type默認(rèn)值為text/html;charset=UTF-8;否則為application/xml;charset=UTF-8;
如果data是 DOMString 類型,content-type默認(rèn)值為text/plain;charset=UTF-8;
如果data是 FormData 類型,content-type默認(rèn)值為multipart/form-data; boundary=[xxx]
如果data是其他類型,則不會(huì)設(shè)置content-type的默認(rèn)值



(3) api.setRequestHeader() -------- 指定一個(gè)http請(qǐng)求的頭部,只有在readyState 為 1 的時(shí)候才能調(diào)用

api.setRequestHeader('name', 'value')
name參數(shù):頭部名稱
value參數(shù):頭部的值
注意: setRequestHeader()方法可以多次調(diào)用,最終的值不是覆蓋override而是追加append
注意: setRequestHeader()方法只有在readyState為1時(shí)才能調(diào)用,即open()方法之后,send()方法之前



(4) api.getResponseHeader() -------- 返回指定的 HTTP 響應(yīng)頭部的值



(5) api.abort() -------- 取消當(dāng)前響應(yīng),關(guān)閉連接并且結(jié)束任何未決的網(wǎng)絡(luò)活動(dòng)
api.abort()將readystate重置為0
應(yīng)用:如果請(qǐng)求用了太長時(shí)間,而且響應(yīng)不再必要的時(shí)候,可以調(diào)用這個(gè)方法。
abort:是中止的意思




(6) api.onreadystatechange() -------- 在 readyState 狀態(tài)改變時(shí)觸發(fā)
api.onreadystatechage() :當(dāng) readyState 為 3 時(shí),它也可能調(diào)用多次。
注意:onreadystatechange()所有字符都是小寫,但readyState又是駝峰寫法

readyState狀態(tài):
0 UNSENT ---- xhr對(duì)象成功構(gòu)造,open()方法未被調(diào)用
1 OPENED ---- open()被調(diào)用,send()還未被調(diào)用,setRequestHeader()可以被調(diào)用
2 HEADERS_RECEIVED ---- send()方法已經(jīng)被調(diào)用, 響應(yīng)頭和響應(yīng)狀態(tài)已經(jīng)返回
3 LOADING ---- 響應(yīng)體(response entity body)正在下載中,此狀態(tài)下通過xhr.response可能已經(jīng)有了響應(yīng)數(shù)據(jù)
4 DONE ---- 整個(gè)數(shù)據(jù)傳輸過程結(jié)束,不管本次請(qǐng)求是成功還是失敗




(7)api.onload  -------- 請(qǐng)求成功時(shí)觸發(fā),此時(shí)readyState為4
重要:所有請(qǐng)求成功的回調(diào):
  1.除了在api.onreadystatechange指定的回調(diào)函數(shù)的readyState===4時(shí)取值
  2.也可以在api.onload事件中取值,如
    api.onload = function () {
      //如果請(qǐng)求成功
      if(api.status == 200){
        //do successCallback
      }
    }
  3.注意判斷api.status===200是有坑的,因?yàn)槌晒r(shí)返回的狀態(tài)碼不止有200,下面的寫法更靠譜
    靠譜的寫法:當(dāng)http狀態(tài)碼為2xx或304時(shí)才認(rèn)為成功
     api.onload = function () {
        //如果請(qǐng)求成功
        if((api.status >= 200 && api.status < 300) || api.status == 304){
            //do successCallback
        }
      }




(8)api.timeout --------- timeout屬性用來設(shè)置過期時(shí)間
問題1:請(qǐng)求的開始時(shí)間怎么確定?是api.onloadstart事件觸發(fā)的時(shí)候,也就是api.send()調(diào)用的時(shí)候
解析:因?yàn)閍pi.open()只是創(chuàng)建了鏈接,當(dāng)并沒有真正傳輸數(shù)據(jù),只有調(diào)用api.send()時(shí)才真正開始傳輸
問題2:什么時(shí)候是請(qǐng)求結(jié)束?
解析:api.loadend事件觸發(fā)時(shí)結(jié)束




(9) 
api.onprogress 下載進(jìn)度信息
api.upload.onprogress 上傳進(jìn)度信息

api.upload.onprogress = function(e) {
    if ( e.lengthComputable ) {
      const present = e.loaded / e.total * 100;
    }
}





-----------------------------------------------------------------------------------------------
1.
問題1:如何獲取response
提供三個(gè)屬性來獲取response:( api.response ) 和 ( api.responseText ) 和 ( responseXML )
api.responseText --- api.responseType='text'、''、不設(shè)置時(shí),xhr對(duì)象上才有此屬性,此時(shí)才能調(diào)用
api.response --- responseType為""或"text"時(shí),值為"";responseType為其他值時(shí),值為 null


2.
問題2:api.responseType有哪些類型
api.responseType類型有:text, document, json, blob, arrayBuffer

get請(qǐng)求

go() {
      console.log('1111111111');
      const api = new XMLHttpRequest();
      api.open('GET',  ----- 初始http請(qǐng)求參數(shù),請(qǐng)求方式,url, 是否異步
      'http://image.baidu.com/channel/listjson?pn=0&rn=30&tag1=明星&tag2=全部&ie=utf8', true);
      api.responseType = 'text';   ------ 文本格式的響應(yīng)
      api.timeout = 5000; ---- 請(qǐng)求過期時(shí)間
      api.setRequestHeader('Content-type', 'application/json'); ----- 必須在open()后,send()前設(shè)置
      api.onreadystatechange = function() { ------ readyState改變時(shí)觸發(fā)
        if ( api.readyState === 4 && this.status === 200) { ---- this指的是api實(shí)例
          console.log(JSON.parse(this.responseText)) ------ this.response也能拿到同樣的數(shù)據(jù)
        }
      }
      // 除了在api.onreadystatechange指指定的會(huì)調(diào)中判斷readyState===4,也可以直接在onload中觸發(fā)
      // 兩種方法都可以
      // 只判斷200狀態(tài)碼不完善,應(yīng)該判斷 2xx 或者 304 則請(qǐng)求成功
      api.onload = function() { 
        if ( api.status >= 200 && api.status < 300 || api.status === 304 ) {
          console.log(JSON.parse(api.responseText), 'onload在請(qǐng)求成功時(shí)觸發(fā)');
        }
      }
      api.send();  ---- 發(fā)送數(shù)據(jù)
    }

http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html 大家
https://segmentfault.com/a/1190000004322487重要
http://www.w3school.com.cn/xmldom/dom_http.asp
https://blog.csdn.net/harryhare/article/details/80778066四種 post 請(qǐng)求的寫法
https://www.cnblogs.com/wgbs25673578/p/5056300.html 可以

(3) <input type="file"/>圖片上傳

  • type屬性:規(guī)定input元素的類型
  • multiple屬性:上傳多張圖片或多個(gè)文件
  • accept屬性:允許上傳的文件類型
    --- image/*:上傳所有圖片類型,注意會(huì)影響速度
    --- image/gif,image/jpeg,image/jpg,image/png ---- 直接寫出來則不會(huì)影響速度
  • 修改默認(rèn)樣式,自帶的樣式很丑,且后面帶有未選擇任何文件的文字,選中后的文件名
    思路: 隱藏<input type="file"/>,使用自定義的button通過$refs去觸發(fā)文件上傳,實(shí)現(xiàn)自定義顯示
  • 上傳同一個(gè)文件,不會(huì)觸發(fā)change事件,即使該文件做過修改
    思路:文件上傳之后,處理完文件,將<input type="file" />的value設(shè)置為null,這樣下次即使上傳的是同一個(gè)文件,仍然會(huì)觸發(fā)change事件
  • display: none不占文檔空間
  • visibility: hidden占據(jù)空間
<template>
  <div class="hello">
    <div>圖片上傳</div>
    <br>
    <div class="upload-file" @click="upFile">
      <div>+</div>
    </div>
    <span v-for="(item, index) in filesName" :key="index" style="margin-right: 10px;">{{item}}</span>
    <input
      type="file"
      class="input-file"
      ref="input-file"
      @change="upload"
      accept="image/gif, image/jpeg"
      multiple="multiple"
    >
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      filesName: []
    }
  },
  methods: {
    upFile() {
      this.$refs['input-file'].click(); // 點(diǎn)擊觸發(fā)input上的click事件,偷梁換柱,觸發(fā)文件上傳
    },
    upload(e) {
      const files = e.target.files || e.dataTransfer.files;
      if (!files.length) {
        this.filesName = [];
        return;
      }
      console.log(files)
      this.filesName = Array.from(files).map(v => v.name);

      // 走上傳接口
      const body = new FormData();
      bocy.append('upload-files', files);
      // ajax send到接口,此處省略
    },
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.input-file {
  display: none;
}
.upload-file {
  background: rgb(41, 129, 245);
  display: inline-block;
  width: 70px;
  height: 30px;
  line-height: 30px;
  margin: 0 auto;
  border-radius: 4px;
  color: white;
  cursor: pointer;
  user-select: none;
}
</style>

https://segmentfault.com/a/1190000013799753
https://www.cnblogs.com/fozero/p/8835628.html
http://www.itdecent.cn/p/c619b81e8f04

(4) <input type='file' />上傳前預(yù)覽圖片

讀取本地圖片,有兩種方法:FileReader 和 URL.createObjectURL
-----URL.createObjectURL性能更好

objectURL = URL.createObjectURL(blob)

  • blob:是用來創(chuàng)建 URL 的 ( File對(duì)象 ) 或 ( Blob對(duì)象 )
    //獲取本地圖片地址
    getObjectURL(file) {  ---------------- 傳入file對(duì)象
      let url = null;
      if (window.createObjectURL != undefined) {  ---------- 判斷不同瀏覽器類型
        // basic
        url = window.createObjectURL(file);
      } else if (window.webkitURL != undefined) {
        // webkit or chrome
        url = window.webkitURL.createObjectURL(file);
      } else if (window.URL != undefined) {
        // mozilla(firefox)
        url = window.URL.createObjectURL(file);
      }
      return url;
    }
完整案例:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <div class="up-btn" @click="upBtn">上傳圖片</div>
    <input
      type="file"
      style="display: none"
      accept="image/jpg, image/png, image/jpeg, image/gif"
      multiple="multiple"
      @change="upImg($event, 'fiel1')"
      ref="input-file"
    />
    <br>
    <div style="color: red">{{errMessage}}</div>
    <div class="imgs-wrap" v-for="(item, index) in imgMessage" :key="index">
      <span><img :src="item.url" alt="" width="200" height="100"></span>
      <span>{{item.name}}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    return {
      imgMessage: [],
      errMessage: ''
    }
  },
  methods: {
    upBtn() {
      this.$refs['input-file'].click()
    },
    upImg(e, type) {
      const files = e.target.files || e.dataTransfer.files;
      this.localImg(files);
    },
    localImg(files) {
      this.errMessage = ''; // 清除錯(cuò)誤信息
      const imgs = Array.from(files)
      .map((item, index) => {
        // 先過濾掉重復(fù)上傳的圖片
        if ( this.imgMessage.map(item => item.name).includes(item.name) ) {
          this.errMessage = '有重復(fù)項(xiàng)';
          return;
        }
        // 本地圖片url
        const localImgUrl = this.getLocalImgUrl(item);
        return {
          name: files[index].name,
          url: localImgUrl,
        }
      })
      .filter(Boolean); // 過濾掉在map中return回來的undefine
      this.imgMessage = this.imgMessage.concat(imgs);
    },
    // 獲取上傳前圖片url
    getLocalImgUrl(file) {
      let localImgUrl = null;
      if ( window.createObjectURL != undefined ) {
        localImgUrl = window.createObjectURL(file);
      } else if ( window.webkitURL != undefined ) {
        localImgUrl = window.webkitURL.createObjectURL(file);
      } else if ( window.URL != undefined ) {
        localImgUrl = window.URL.createObjectURL(file);
      }
      return localImgUrl;
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.up-btn {
  display: inline-block;
  margin: 20px 10px auto;
  padding: 6px 20px;
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
  color: white;
  background: rgb(255, 0, 221);
}
.up-btn:hover {
  background: rgb(133, 2, 115);
}
.imgs-wrap {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
}
</style>


QQ截圖20190309185701.jpg

https://blog.csdn.net/weixin_38023551/article/details/78318532
https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL
https://www.cnblogs.com/stephenykk/p/3558887.html

(5) 上傳進(jìn)度信息和下載進(jìn)度信息

onprogress事件用來返回進(jìn)度信息

  • xhr.onprogress = updateProgress; -------------------------- 下載進(jìn)度信息
  • xhr.upload.onprogress = updateProgress; ---------------- 上傳進(jìn)度信息
api.upload.onprogress=function (e){
  if(e.lengthComputable){
     var precent=100 * e.loaded/e.total;
     console.log(precent);
  }
}


解析:
1. e.total -------------------- 傳輸?shù)目傋止?jié)
2. e.loaded ------------------- 已經(jīng)傳輸?shù)淖止?jié)
3. e.lengthComputable --------- 指定上傳總數(shù)居的大小(上傳文件總大小已知)
4. e.transferSpeed ------------ 傳輸速度
5. e.timeRemaining ------------ 剩余時(shí)間

(5) blob對(duì)象

blob對(duì)象表示一個(gè)不可變的,原始數(shù)據(jù)的類文件對(duì)象。File接口基于Blob,Blob對(duì)象可以看作是存放二進(jìn)制數(shù)據(jù)的容器

##### new Blob(dataArr:Array<any>, opt:{type:string});

第一個(gè)參數(shù):是要添加到Bolb對(duì)象的數(shù)據(jù)數(shù)組,注意:是一個(gè)數(shù)組
第二個(gè)參數(shù):配置對(duì)象
  • 構(gòu)造函數(shù):Blob()
  • 返回一個(gè)新創(chuàng)建的 Blob 對(duì)象,其內(nèi)容由參數(shù)中給定的數(shù)組串聯(lián)組成
  • 屬性:
    Blob.size:blob中包含數(shù)據(jù)的大小
    Blob.type:一個(gè)字符串,表明該Blob的對(duì)象所包含數(shù)據(jù)MIME類型

http://www.cnblogs.com/hhhyaaon/p/5928152.html 應(yīng)用場景( 分片上傳 )
https://blog.csdn.net/qq_42842709/article/details/82500029

(6) new Image() ----- 用于生成 HTMLImageElement 實(shí)例

參數(shù):new Image(width, height) ---- 接受兩個(gè)參數(shù),分別表示寬度和高度
屬性:src,currentSrc,alt,srcset,sizes
注意:用js生成的img實(shí)例,并不在文檔中,需要手動(dòng)插入
事件:

  • onload:圖像加載完成,會(huì)觸發(fā)onload屬性指定的回調(diào)函數(shù)
  • onerr0r:圖像加載完成,同時(shí)也會(huì)觸發(fā)onerror屬性指定的回調(diào)函數(shù)

注意事項(xiàng):
生成的image一定要手動(dòng)插入到文檔,onload和onerror的回調(diào)都要指定

  mounted() {
    const limg = require('../images/1.jpg');
    const img = new Image(200, 200);   ------------- 參數(shù)分別是 width 和 height
    img.src = limg;    ---------------- 除了src,還有currentSrc表示當(dāng)前src,因?yàn)閟rc可以動(dòng)態(tài)指定
    img.onload = function() {
      console.log('加載完成');
      document.body.appendChild(img);   -------------- 插入文檔
    }
    img.onerror = function() {
      console.log('錯(cuò)誤')
    }
  }






完整示例

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <div class="up-btn" @click="upBtn">上傳圖片</div>
    <input
      type="file"
      style="display: none"
      accept="image/jpg, image/png, image/jpeg, image/gif"
      multiple="multiple"
      @change="upImg($event, 'fiel1')"
      ref="input-file"
    />
    <br>
    <div style="color: red">{{errMessage}}</div>
    <div>上傳進(jìn)度:{{persent}}</div>
    <div class="imgs-wrap" v-for="(item, index) in imgMessage" :key="index">
      <span><img :src="item.url" alt="" width="200" height="100"></span>
      <span>{{item.name}}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    return {
      imgMessage: [],
      errMessage: '',
      persent: 0,
    }
  },
  methods: {
    upBtn() {
      this.$refs['input-file'].click()
    },
    upImg(e, type) {
      const files = e.target.files || e.dataTransfer.files;
      this.viewLocalImages(files);
      this.uploadAllImg(files);
    },
    // 獲取上傳前圖片url
    getLocalImgUrl(file) {
      let localImgUrl = null;
      if ( window.createObjectURL != undefined ) {
        localImgUrl = window.createObjectURL(file);
      } else if ( window.webkitURL != undefined ) {
        localImgUrl = window.webkitURL.createObjectURL(file);
      } else if ( window.URL != undefined ) {
        localImgUrl = window.URL.createObjectURL(file);
      }
      return localImgUrl;
    },
    // 預(yù)覽上傳前圖片
    viewLocalImages(files) {
      this.errMessage = ''; // 清除錯(cuò)誤信息
      const imgs = Array.from(files)
      .map((item, index) => {
        // 先過濾掉重復(fù)上傳的圖片
        if ( this.imgMessage.map(item => item.name).includes(item.name) ) {
          this.errMessage = '有重復(fù)項(xiàng)';
          return;
        }
        // 本地圖片url
        const localImgUrl = this.getLocalImgUrl(item);
        return {
          name: files[index].name,
          url: localImgUrl,
        }
      })
      .filter(Boolean); // 過濾掉在map中return回來的undefine
      this.imgMessage = this.imgMessage.concat(imgs);
    },
    // 上傳圖片
    uploadAllImg(files) {
      // 構(gòu)造表單
      const form = new FormData();
      Array.from(files).forEach((item, index) => {
        form.append(`file${index}`, item)
      });
      // ajax
      const api = new XMLHttpRequest();
      api.open('POST', '............cmsiw/attachment/upload', true);
      // api.setRequestHeader()必須在open()之后,send()之前
      api.setRequestHeader('Authorization', 'Bearer .....');
      // api.setRequestHeader('Content-type', 'application/json;charset=utf-8');
      api.responseType = 'text';
      api.timeout = 10000;
      // 上傳進(jìn)度
      const that = this;
      api.upload.onprogress = function(e) {
        if (e.lengthComputable) {
          const persent = e.loaded / e.total * 100;
          console.log(persent);
          that.persent = persent;
        }
      }
      // 上傳成功
      api.onreadystatechange = function() {
        if (api.readyState === 4 && api.status === 200 ) { // 200用最好 >=200 && <300 || 304代替
          console.log(JSON.parse(api.responseText));
        }
      }
      api.send(form);
    }
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.up-btn {
  display: inline-block;
  margin: 20px 10px auto;
  padding: 6px 20px;
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
  color: white;
  background: rgb(255, 0, 221);
}
.up-btn:hover {
  background: rgb(133, 2, 115);
}
.imgs-wrap {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
}
</style>

QQ截圖20190309202243.jpg

復(fù)習(xí): 2019/3/22

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script>
    window.onload = function() {
      const fileDom = document.getElementById('file');
      // 本地預(yù)覽
      fileDom.addEventListener('change', function(e) {
        const files = e.target.files;
        const file = e.target.files[0];
        console.log(files);
        // 插入名字
        const imgName = Array.from(files)[0].name;
        const imgNameDom = document.createElement('span')
        imgNameDom.innerHTML = imgName;
        document.getElementById('content').appendChild(imgNameDom);
        // 插入圖片
        Array.from(files).forEach(item => {
          let url = null;
          if (window.createObjectURL) {
            url = window.createObjectURL(item);
          } else if ( window.URL) {
            url = window.URL.createObjectURL(item);
          } else if ( window.webkitURL) {
            url = window.webkitURL.createObjectURL(item);
          }
          const img = new Image(40, 40);
          img.src = url;
          img.onload = function() {
            document.getElementById('content').appendChild(img);
          }
        })
      }, false)
    };
  </script>
</head>
<body>
  <div id="file-wrap" style="position: relative">
    <div
      style="
        cursor: pointer;
        background:blueviolet;
        color: white;
        width: 100px;
        text-align: center;
        margin: 0 auto;
        border-radius: 8px;
        height: 30px;
        line-height: 30px;
    ">上傳</div>
    <input
      type="file"
      multiple="multiple"
      accept="image/*"
      style="
        opacity: 0;
        position: absolute;
        border: 1px solid red;
        z-index: 99999;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        height: 30px;
        width: 100px;
        "
        id="file"
    >
  </div>
  <br>
  <div id="content" style="width: 600px; height: 600px; border: 1px solid black; margin: 0 auto">
  </div>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容