如何利用原生JS實(shí)現(xiàn)圖片預(yù)覽加上傳(前后端交互)
前言
最近在寫vue項(xiàng)目的時(shí)候發(fā)現(xiàn)了個(gè)Vant的一個(gè)upload的圖片上傳的組件,就好奇了一下下,于是萌生了一個(gè)自己手寫一個(gè)圖片上傳的組件的想法,您猜怎么著,還真給我實(shí)現(xiàn)了,那今天就和大家分享一下,大家有興趣的可以了解一下啦,寫進(jìn)項(xiàng)目中可能會(huì)是個(gè)加分點(diǎn)哦??!
我們知道文件上傳是需要前后端交互的,所以我這邊給出前后端代碼。 文件上傳大致分為以下幾個(gè)步驟
- 前端文件選擇上傳的文件類型
- 拿到文件信息
- 將選擇的文件(視頻或圖片)在前端頁面預(yù)覽出來
- 將文件發(fā)上傳到后端服務(wù)器
- 客戶端能夠訪問到上傳到服務(wù)器端的文件
效果大致如下

前端代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.button{
margin-right: 20px;
}
#preview {
display: flex;
flex-wrap: wrap;
width: 1500px;
padding: 0 10px;
justify-content: start;
}
.icon-po {
overflow: hidden;
position: relative;
width: 300px;
height: 300px;
margin-right: 20px;
margin-top: 20px;
}
.icon-close {
position: absolute;
right: 5%;
top: 5%;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: red;
color: #fff;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.pic{
width: 300px;
height: 300px;
}
</style>
</head>
<body>
選擇文件(允許多文件):
<input type="file" id="f1" multiple> //multiple開啟多選
<button type="button" class="button" id="btn-submit">預(yù)覽圖片</button>
<button type="button" id="complate">上傳圖片</button>
<div id="preview"></div>
<br />
<script>
let e = 0
let fd = new FormData() //文件傳輸一定要使用ForData對(duì)象
let fL = []
function submitUpload() {
let fileList = document.getElementById('f1').files
for (let i = 0; i < fileList.length; i++) {
fL.push(fileList[i])
}
//渲染出圖片
//FileReader 讀取文件的方式為readAsDataURL時(shí)會(huì)觸發(fā)
function readAndPreview(file) {
// 確保 `file.name` 符合我們要求的擴(kuò)展名
if (/\.(jpe?g|png|gif|mp4)$/i.test(file.name)) {
//FileReader 對(duì)象允許Web應(yīng)用程序異步讀取存儲(chǔ)在用戶計(jì)算機(jī)上的文件
//(或原始數(shù)據(jù)緩沖區(qū))的內(nèi)容
//這里是讀取到input返回出來的fileList中文件
var reader = new FileReader();
//該事件在FileReader對(duì)象讀取操作完成時(shí)觸發(fā)
reader.addEventListener("load", function () {
//創(chuàng)建出兩個(gè)div 一個(gè)用做父容器 一個(gè)做刪除按鈕
var div1 = document.createElement('div');
var div2 = document.createElement('div');
//創(chuàng)建一個(gè)Image Dom 用來做渲染圖片
var dom = new Image() //當(dāng)然要是想渲染視頻的話就創(chuàng)建一個(gè)video標(biāo)簽就好啦
//給創(chuàng)建的Dom添加類名用于設(shè)計(jì)樣式
div1.className = 'icon-po'
div2.className = 'icon-close'
div2.innerHTML = 'X'
dom.className = 'pic
//給生成的每個(gè)圖片盒子添加index屬性
div1.index = div2.index = e
e++
//給刪除按鈕添加上點(diǎn)擊事件
div2.onclick = (e) => {
console.log(e.target.index)
div1.remove()
fL.splice(e.target.index, 1, "")
console.log(fL);
}
//設(shè)置img的樣式
dom.width = 300;
dom.height = 300;
dom.title = file.name;
//該 result屬性包含作為數(shù)據(jù)的數(shù)據(jù): URL將文件的數(shù)據(jù)表示為 base64 編碼的字符串。
dom.src = this.result;
//將創(chuàng)建出的dom添加進(jìn)對(duì)應(yīng)的dom中去
div1.appendChild(dom)
div1.appendChild(div2)
preview.appendChild(div1);
}, false);
//調(diào)用readAsDataURL()方法拿到result
reader.readAsDataURL(file);
}
}
//拿到input中的fileList中的文件對(duì)象進(jìn)行渲染出圖片
if (fileList) {
[].forEach.call(fileList, readAndPreview);
}
if (!fileList.length) {
alert('請(qǐng)選擇文件')
return
}
}
function complate() {
//篩選出未刪除的文件
fL.forEach(async (item) => {
if (typeof item === "string") {
console.log(item);
} else {
fd.append('files', item)//將沒有別刪除的文件加入到FormData對(duì)象中
}
})
//向后端發(fā)送請(qǐng)求
let xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:3007/', true)
xhr.send(fd)
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
let obj = JSON.parse(xhr.responseText)
console.log(obj);
}
}
}
document.getElementById('btn-submit').addEventListener('click', submitUpload)
document.getElementById('complate').addEventListener('click', complate)
</script>
</body>
</html>后端代碼
/**
* 服務(wù)入口
*/
var koaStatic = require('koa-static');
var path = require('path');
var koaBody = require('koa-body');
var fs = require('fs');
var Koa = require('koa2');
var cors = require('koa2-cors') //解決跨域
var app = new Koa();
var port = process.env.PORT || '3007';
var uploadHost = `http://localhost:3001/uploads/`;
app.use(cors())
app.use(koaBody({
//koa-body 是一個(gè)可以幫助解析 http 中 body 的部分的中間件,包括 json、表單、文本、文件等。
formidable: {
//設(shè)置文件的默認(rèn)保存目錄,不設(shè)置則保存在系統(tǒng)臨時(shí)目錄下
uploadDir: path.resolve(__dirname, './static')
},
multipart: true // 支持文件上傳
}));
app.use(koaStatic(
//讓我們更加快速訪問服務(wù)中的靜態(tài)資源
path.resolve(__dirname, './static')
));
//二次處理文件,修改名稱
app.use((ctx) => {
console.log(ctx.request.files);
var files = ctx.request.files.files;//得到上傳文件的數(shù)組
var result = [];
console.log(files);
let baseUrl = 'http://192.168.10.4:3007/'
if (!Array.isArray(files)) {//單文件上傳容錯(cuò)
files = [files];
}
files && files.forEach(item => {
var path = item.path.replace(/\\/g, '/');
var fname = item.name;//原文件名稱
var nextPath = path + fname;
if (item.size > 0 && path) {
//得到擴(kuò)展名
var extArr = fname.split('.');
var ext = extArr[extArr.length - 1];
var nextPath = path + '.' + ext;
//重命名文件
fs.renameSync(path, nextPath);
console.log(baseUrl + nextPath.slice(nextPath.lastIndexOf('/') + 1))
result.push(baseUrl + nextPath.slice(nextPath.lastIndexOf('/') + 1));
}
});
ctx.body = {//向前端返回圖片上傳后的地址
"fileUrl":`${JSON.stringify(result)}`
};
})
app.listen(port, () => {
console.log('服務(wù)已啟動(dòng)3007');
})
總結(jié)
代碼我都做好詳細(xì)注釋啦,使用到的官方對(duì)象的作用我也給你總結(jié)出來了給你們偷個(gè)懶??!相信大家花點(diǎn)時(shí)間看一下就能懂啦,最后我再叨叨幾句~
- 前端
- js使用input type ="file"可以上傳文件
- 讀取文件完成后創(chuàng)建出幾個(gè)DOM用來預(yù)覽圖片,配置好相應(yīng)的功能需求
- FileReader 對(duì)象允許Web應(yīng)用程序異步讀取存儲(chǔ)在用戶計(jì)算機(jī)上的文件(或原始數(shù)據(jù)緩沖區(qū))的內(nèi)容,使用 [File] 或 [Blob] 對(duì)象指定要讀取的文件或數(shù)據(jù)。
- readAsDataURL 方法會(huì)讀取指定的 [Blob]/Blob) 或 [File] 對(duì)象。讀取操作完成的時(shí)候,[readyState] 會(huì)變成已完成DONE,并觸發(fā) loadend (en-US) 事件,同時(shí) [result] 屬性將包含一個(gè)data:URL格式的字符串(base64編碼)以表示所讀取文件的內(nèi)容。
- FormData接口提供了一種方法來輕松構(gòu)造一組表示表單字段及其值的鍵/值對(duì),然后可以使用該[XMLHttpRequest.send()]方法向后端發(fā)送文件。
- 后端
- 后端這里是使用Koa寫的
- 安裝koa2-cors防止跨域
- 安裝koa-body拿到前端傳來的文件,并存在相應(yīng)的目錄下
- 安裝Koa-static然后端更加方便的訪問到上傳的文件地址
到此這篇關(guān)于如何利用原生JS實(shí)現(xiàn)圖片預(yù)覽加上傳的文章就介紹到這了,更多相關(guān)JS實(shí)現(xiàn)圖片預(yù)覽上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
uniapp自定義頁面跳轉(zhuǎn)loading的實(shí)現(xiàn)代碼
有些頁面加載起來比較慢,為了加強(qiáng)用戶體驗(yàn)效果,所以一般都會(huì)做一個(gè)頁面加載等待的提示,頁面加載完成后消失,下面這篇文章主要給大家介紹了關(guān)于uniapp自定義頁面跳轉(zhuǎn)loading的實(shí)現(xiàn)代碼,需要的朋友可以參考下2023-06-06
JavaScript實(shí)現(xiàn)的類字典插入或更新方法實(shí)例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的類字典插入或更新方法,實(shí)例分析了javascript針對(duì)類字典的插入與更新技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
addeventlistener監(jiān)聽scroll跟touch(實(shí)例講解)
下面小編就為大家?guī)硪黄猘ddeventlistener監(jiān)聽scroll跟touch(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
JS實(shí)現(xiàn)瀏覽器點(diǎn)擊下載圖片功能案例分析【親測(cè)有效】
這篇文章主要介紹了JS實(shí)現(xiàn)瀏覽器點(diǎn)擊下載圖片功能,對(duì)比分析了同源與不同源兩種解決方案,并以實(shí)際案例形式分析了不同源情況下針對(duì)文件點(diǎn)擊下載的具體實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2023-04-04
js+canvas實(shí)現(xiàn)轉(zhuǎn)盤效果(兩個(gè)版本)
這篇文章主要為大家詳細(xì)介紹了兩個(gè)版本的js+canvas實(shí)現(xiàn)轉(zhuǎn)盤效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09
Javascript讀取上傳文件內(nèi)容/類型/字節(jié)數(shù)
這篇文章主要為大家詳細(xì)介紹了Javascript讀取上傳文件內(nèi)容/類型/字節(jié)數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04

