日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

文件上传攻略

發布時間:2023/12/31 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 文件上传攻略 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


(1)原理概述

就是根據 http 協議的規范和定義,完成請求消息體的封裝和消息體的解析,然后將二進制內容保存到文件。

我們都知道如果要上傳一個文件,需要把 form 標簽的enctype設置為multipart/form-data,同時method必須為post方法。

那么multipart/form-data表示什么呢?

multipart互聯網上的混合資源,就是資源由多種元素組成,form-data表示可以使用HTML Forms 和 POST 方法上傳文件,具體的定義可以參考RFC 7578。

multipart/form-data 結構

看下 http 請求的消息體

解析


(2)最原始的文件上傳

使用 form 表單上傳文件

在 ie時代,如果實現一個無刷新的文件上傳那可是費老勁了,大部分都是用 iframe 來實現局部刷新或者使用 flash 插件來搞定,在那個時代 ie 就是最好用的瀏覽器(別無選擇)。

這種方式上傳文件,不需要 js ,而且沒有兼容問題,所有瀏覽器都支持,就是體驗很差,導致頁面刷新,頁面其他數據丟失。

HTML

<form method="post" action="http://localhost:8100" enctype="multipart/form-data">選擇文件:<input type="file" name="f1"/> input 必須設置 name 屬性,否則數據無法發送<br/> <br/>標題:<input type="text" name="title"/><br/><br/><br/><button type="submit" id="btn-0">上 傳</button></form>

(3)文件上傳接口

NODE

/*** 服務入口*/ var http = require('http'); var koaStatic = require('koa-static'); var path = require('path'); var koaBody = require('koa-body');//文件保存庫 var fs = require('fs'); var Koa = require('koa2');var app = new Koa(); var port = process.env.PORT || '8100';var uploadHost= `http://localhost:${port}/uploads/`;app.use(koaBody({formidable: {//設置文件的默認保存目錄,不設置則保存在系統臨時目錄下 osuploadDir: path.resolve(__dirname, '../static/uploads')},multipart: true // 開啟文件上傳,默認是關閉 }));//開啟靜態文件訪問 app.use(koaStatic(path.resolve(__dirname, '../static') ));//文件二次處理,修改名稱 app.use((ctx) => {var file = ctx.request.files.f1;//得道文件對象var path = file.path;var fname = file.name;//原文件名稱var nextPath = path+fname;if(file.size>0 && path){//得到擴展名var extArr = fname.split('.');var ext = extArr[extArr.length-1];var nextPath = path+'.'+ext;//重命名文件fs.renameSync(path, nextPath);}//以 json 形式輸出上傳文件地址ctx.body = `{"fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"}`; });/*** http server*/ var server = http.createServer(app.callback()); server.listen(port); console.log('demo1 server start ...... ');

(4)多文件上傳

HTML

//設置 multiple屬性 <input type="file" name="f1" multiple/>

NODE

服務端也需要進行簡單的調整,由單文件對象變為多文件數組,然后進行遍歷處理。

//二次處理文件,修改名稱 app.use((ctx) => {var files = ctx.request.files.f1;// 多文件, 得到上傳文件的數組var result=[];//遍歷處理files && files.forEach(item=>{var path = item.path;var fname = item.name;//原文件名稱var nextPath = path + fname;if (item.size > 0 && path) {//得到擴展名var extArr = fname.split('.');var ext = extArr[extArr.length - 1];var nextPath = path + '.' + ext;//重命名文件fs.renameSync(path, nextPath);//文件可訪問路徑放入數組result.push(uploadHost+ nextPath.slice(nextPath.lastIndexOf('/') + 1));}});//輸出 json 結果ctx.body = `{"fileUrl":${JSON.stringify(result)}}`; })

(5)局部刷新 - iframe

HTML

<iframe id="temp-iframe" name="temp-iframe" src="" style="display:none;"></iframe><form method="post" target="temp-iframe" action="http://localhost:8100" enctype="multipart/form-data">選擇文件(可多選):<input type="file" name="f1" id="f1" multiple/><br/> input 必須設置 name 屬性,否則數據無法發送<br/> <br/>標題:<input type="text" name="title"/><br/><br/><br/><button type="submit" id="btn-0">上 傳</button></form><script>var iframe = document.getElementById('temp-iframe'); iframe.addEventListener('load',function () {var result = iframe.contentWindow.document.body.innerText;//接口數據轉換為 JSON 對象var obj = JSON.parse(result);if(obj && obj.fileUrl.length){alert('上傳成功');}console.log(obj); });</script>

(6)無刷新上傳

HTML

<div>選擇文件(可多選):<input type="file" id="f1" multiple/><br/><br/><button type="button" id="btn-submit">上 傳</button> </div>

JS xhr

<script>function submitUpload() {//獲得文件列表,注意這里不是數組,而是對象var fileList = document.getElementById('f1').files;if(!fileList.length){alert('請選擇文件');return;}var fd = new FormData(); //構造FormData對象fd.append('title', document.getElementById('title').value);//多文件上傳需要遍歷添加到 fromdata 對象for(var i =0;i<fileList.length;i++){fd.append('f1', fileList[i]);//支持多文件上傳}var xhr = new XMLHttpRequest(); //創建對象xhr.open('POST', 'http://localhost:8100/', true);xhr.send(fd);//發送時 Content-Type默認就是: multipart/form-data; xhr.onreadystatechange = function () {console.log('state change', xhr.readyState);if (this.readyState == 4 && this.status == 200) {var obj = JSON.parse(xhr.responseText); //返回值console.log(obj);if(obj.fileUrl.length){alert('上傳成功');}}}}//綁定提交事件document.getElementById('btn-submit').addEventListener('click',submitUpload); </script>

JS Fetch

fetch('http://localhost:8100/', {method: 'POST',body: fd}).then(response => response.json()).then(response =>{console.log(response);if (response.fileUrl.length) {alert('上傳成功');}} ).catch(error => console.error('Error:', error));

(7)多文件,單進度


特別提醒

HTML

<div>選擇文件(可多選):<input type="file" id="f1" multiple/><br/><br/><div id="progress"><span class="red"></span></div><button type="button" id="btn-submit">上 傳</button></div>

JS

<script>function submitUpload() {var progressSpan = document.getElementById('progress').firstElementChild;var fileList = document.getElementById('f1').files;progressSpan.style.width='0';progressSpan.classList.remove('green');if(!fileList.length){alert('請選擇文件');return;}var fd = new FormData(); //構造FormData對象fd.append('title', document.getElementById('title').value);for(var i =0;i<fileList.length;i++){fd.append('f1', fileList[i]);//支持多文件上傳}var xhr = new XMLHttpRequest(); //創建對象xhr.open('POST', 'http://10.70.65.235:8100/', true);xhr.onreadystatechange = function () {console.log('state change', xhr.readyState);if (xhr.readyState == 4) {var obj = JSON.parse(xhr.responseText); //返回值console.log(obj);if(obj.fileUrl.length){//alert('上傳成功');}}}xhr.onprogress=updateProgress;xhr.upload.onprogress = updateProgress;function updateProgress(event) {console.log(event);if (event.lengthComputable) {var completedPercent = (event.loaded / event.total * 100).toFixed(2);progressSpan.style.width= completedPercent+'%';progressSpan.innerHTML=completedPercent+'%';if(completedPercent>90){//進度條變色progressSpan.classList.add('green');}console.log('已上傳',completedPercent);}}//注意 send 一定要寫在最下面,否則 onprogress 只會執行最后一次 也就是100%的時候xhr.send(fd);//發送時 Content-Type默認就是: multipart/form-data; }//綁定提交事件document.getElementById('btn-submit').addEventListener('click',submitUpload);</script>

(8)多文件上傳+預覽+取消

上一個栗子的多文件上傳只有一個進度條,有些需求可能會不大一樣,需要觀察到每個文件的上傳進度,并且可以終止上傳。

HTML

<div>選擇文件(可多選):<div class="addfile">添加文件<input type="file" id="f1" multiple /></div><div class="img-box"></div><button type="button" id="btn-submit">上 傳</button></div>

JS

<script>//更改網絡 為慢3g,就可以比較明顯的看到進度條了var fileMaxCount=6;var imgBox =document.getElementsByClassName('img-box')[0];var willUploadFile=[];//保存待上傳的文件以及相關附屬信息document.getElementById('f1').addEventListener('change',function (e) {var fileList = document.getElementById('f1').files;if (willUploadFile.length > fileMaxCount || fileList.length>fileMaxCount || (willUploadFile.length+ fileList.length>fileMaxCount)) {alert('最多只能上傳' + fileMaxCount + '張圖');return;}for (var i = 0; i < fileList.length; i++) {var f = fileList[i];//先預覽圖片var img = document.createElement('img');var item = document.createElement('div');var progress = document.createElement('div');progress.className='progress';progress.innerHTML = '<span class="red"></span><button type="button">Abort</button>';item.className='item';img.src = window.URL.createObjectURL(f);img.onload = function () {//顯示要是否這塊兒內存window.URL.revokeObjectURL(this.src);}item.appendChild(img);item.appendChild(progress);imgBox.appendChild(item);willUploadFile.push({file:f,item,progress});}});function xhrSend({file, progress}) {var progressSpan = progress.firstElementChild;var btnCancel = progress.getElementsByTagName('button')[0];btnCancel.removeEventListener('click',function(e) {});btnCancel.addEventListener('click',function(e) {if(xhr && xhr.readyState!==4){//取消上傳xhr.abort();} });progressSpan.style.width='0';progressSpan.classList.remove('green');var fd = new FormData(); //構造FormData對象fd.append('f1',file);var xhr = new XMLHttpRequest(); //創建對象xhr.open('POST', 'http://localhost:8100/', true);xhr.onreadystatechange = function () {console.log('state change', xhr.readyState);//調用 abort 后,state 立即變成了4,并不會變成0//增加自定義屬性 xhr.uploadedif (xhr.readyState == 4 && xhr.uploaded) {var obj = JSON.parse(xhr.responseText); //返回值console.log(obj);if(obj.fileUrl.length){//alert('上傳成功');}}}xhr.onprogress=updateProgress;xhr.upload.onprogress = updateProgress;function updateProgress(event) {if (event.lengthComputable) {var completedPercent = (event.loaded / event.total * 100).toFixed(2);progressSpan.style.width= completedPercent+'%';progressSpan.innerHTML=completedPercent+'%';if(completedPercent>90){//進度條變色progressSpan.classList.add('green');}if(completedPercent>=100){xhr.uploaded=true;}console.log('已上傳',completedPercent);}}//注意 send 一定要寫在最下面,否則 onprogress 只會執行最后一次 也就是100%的時候xhr.send(fd);//發送時 Content-Type默認就是: multipart/form-data; return xhr;}//文件上傳function submitUpload(willFiles) {if(!willFiles.length){return;}//遍歷文件信息進行上傳willFiles.forEach(function (item) {xhrSend({file:item.file,progress:item.progress});});}//綁定提交事件document.getElementById('btn-submit').addEventListener('click',function () {submitUpload(willUploadFile);});</script>


(9)拖拽上傳

html5的出現,讓拖拽上傳交互成為可能,現在這樣的體驗也屢見不鮮。

HTML

<div class="drop-box" id="drop-box">拖動文件到這里,開始上傳</div><button type="button" id="btn-submit">上 傳</button>

JS

<script>var box = document.getElementById('drop-box');//禁用瀏覽器的拖放默認行為document.addEventListener('drop',function (e) {console.log('document drog');e.preventDefault();});//設置拖拽事件function openDropEvent() {box.addEventListener("dragover",function (e) {console.log('elemenet dragover');box.classList.add('over');e.preventDefault();});box.addEventListener("dragleave", function (e) {console.log('elemenet dragleave');box.classList.remove('over');e.preventDefault();});box.addEventListener("drop", function (e) {e.preventDefault(); //取消瀏覽器默認拖拽效果var fileList = e.dataTransfer.files; //獲取拖拽中的文件對象var len=fileList.length;//用來獲取文件的長度(其實是獲得文件數量)//檢測是否是拖拽文件到頁面的操作if (!len) {box.classList.remove('over');return;}box.classList.add('over');window.willUploadFileList=fileList;}, false);}openDropEvent();function submitUpload() {var fileList = window.willUploadFileList||[];if(!fileList.length){alert('請選擇文件');return;}var fd = new FormData(); //構造FormData對象for(var i =0;i<fileList.length;i++){fd.append('f1', fileList[i]);//支持多文件上傳}var xhr = new XMLHttpRequest(); //創建對象xhr.open('POST', 'http://localhost:8100/', true);xhr.onreadystatechange = function () {if (xhr.readyState == 4) {var obj = JSON.parse(xhr.responseText); //返回值if(obj.fileUrl.length){alert('上傳成功');}}}xhr.send(fd);//發送}//綁定提交事件document.getElementById('btn-submit').addEventListener('click',submitUpload);</script>

(11)剪貼板上傳

HTML

<div class="editor-box" id="editor-box" contenteditable="true" >可以直接粘貼圖片到這里直接上傳</div>

JS

//光標處插入 dom 節點function insertNodeToEditor(editor,ele) {//插入dom 節點var range;//記錄光標位置對象var node = window.getSelection().anchorNode;// 這里判斷是做是否有光標判斷,因為彈出框默認是沒有的if (node != null) {range = window.getSelection().getRangeAt(0);// 獲取光標起始位置range.insertNode(ele);// 在光標位置插入該對象} else {editor.append(ele);}}var box = document.getElementById('editor-box');//綁定paste事件box.addEventListener('paste',function (event) {var data = (event.clipboardData || window.clipboardData);var items = data.items;var fileList = [];//存儲文件數據if (items && items.length) {// 檢索剪切板itemsfor (var i = 0; i < items.length; i++) {console.log(items[i].getAsFile());fileList.push(items[i].getAsFile());}}window.willUploadFileList = fileList;event.preventDefault();//阻止默認行為submitUpload();}); function submitUpload() {var fileList = window.willUploadFileList||[];var fd = new FormData(); //構造FormData對象for(var i =0;i<fileList.length;i++){fd.append('f1', fileList[i]);//支持多文件上傳}var xhr = new XMLHttpRequest(); //創建對象xhr.open('POST', 'http://localhost:8100/', true);xhr.onreadystatechange = function () {if (xhr.readyState === 4) {var obj = JSON.parse(xhr.responseText); //返回值console.log(obj);if(obj.fileUrl.length){var img = document.createElement('img');img.src= obj.fileUrl[0];img.style.width='100px';insertNodeToEditor(box,img);// alert('上傳成功');}}}xhr.send(fd);//發送}

(12)大文件上傳-分片

HTML

代碼略,只需要一個 input file 標簽。

JS

//分片邏輯 像操作字符串一樣var start=0,end=0;while (true) {end+=chunkSize;var blob = file.slice(start,end);start+=chunkSize;if(!blob.size){//截取的數據為空 則結束//拆分結束break;}chunks.push(blob);//保存分段數據}<script>function submitUpload() {var chunkSize=2*1024*1024;//分片大小 2Mvar file = document.getElementById('f1').files[0];var chunks=[], //保存分片數據token = (+ new Date()),//時間戳name =file.name,chunkCount=0,sendChunkCount=0;//拆分文件 像操作字符串一樣if(file.size>chunkSize){//拆分文件var start=0,end=0;while (true) {end+=chunkSize;var blob = file.slice(start,end);start+=chunkSize;if(!blob.size){//截取的數據為空 則結束//拆分結束break;}chunks.push(blob);//保存分段數據}}else{chunks.push(file.slice(0));}chunkCount=chunks.length;//分片的個數 //沒有做并發限制,較大文件導致并發過多,tcp 鏈接被占光 ,需要做下并發控制,比如只有4個在請求在發送for(var i=0;i< chunkCount;i++){var fd = new FormData(); //構造FormData對象fd.append('token', token);fd.append('f1', chunks[i]);fd.append('index', i);xhrSend(fd,function () {sendChunkCount+=1;if(sendChunkCount===chunkCount){//上傳完成,發送合并請求console.log('上傳完成,發送合并請求');var formD = new FormData();formD.append('type','merge');formD.append('token',token);formD.append('chunkCount',chunkCount);formD.append('filename',name);xhrSend(formD);}});}}function xhrSend(fd,cb) {var xhr = new XMLHttpRequest(); //創建對象xhr.open('POST', 'http://localhost:8100/', true);xhr.onreadystatechange = function () {console.log('state change', xhr.readyState);if (xhr.readyState == 4) {console.log(xhr.responseText);cb && cb();}}xhr.send(fd);//發送}//綁定提交事件document.getElementById('btn-submit').addEventListener('click',submitUpload); </script>

注意點

//二次處理文件,修改名稱 app.use((ctx) => {var body = ctx.request.body;var files = ctx.request.files ? ctx.request.files.f1:[];//得到上傳文件的數組var result=[];var fileToken = ctx.request.body.token;// 文件標識var fileIndex=ctx.request.body.index;//文件順序if(files && !Array.isArray(files)){//單文件上傳容錯files=[files];}files && files.forEach(item=>{var path = item.path;var fname = item.name;//原文件名稱var nextPath = path.slice(0, path.lastIndexOf('/') + 1) + fileIndex + '-' + fileToken;if (item.size > 0 && path) {//得到擴展名var extArr = fname.split('.');var ext = extArr[extArr.length - 1];//var nextPath = path + '.' + ext;//重命名文件fs.renameSync(path, nextPath);result.push(uploadHost+nextPath.slice(nextPath.lastIndexOf('/') + 1));}});if(body.type==='merge'){//合并分片文件var filename = body.filename,chunkCount = body.chunkCount,folder = path.resolve(__dirname, '../static/uploads')+'/';var writeStream = fs.createWriteStream(`${folder}${filename}`);var cindex=0;//合并文件function fnMergeFile(){var fname = `${folder}${cindex}-${fileToken}`;var readStream = fs.createReadStream(fname);readStream.pipe(writeStream, { end: false });readStream.on("end", function () {fs.unlink(fname, function (err) {if (err) {throw err;}});if (cindex+1 < chunkCount){cindex += 1;fnMergeFile();}});}fnMergeFile();ctx.body='merge ok 200';}});

(13)大文件上傳-斷點續傳

生成 hash 過程肯定也會耗費資源,但是和重新上傳相比可以忽略不計了

JS
模擬分段保存,本地保存到localStorage

//獲得本地緩存的數據function getUploadedFromStorage(){return JSON.parse( localStorage.getItem(saveChunkKey) || "{}");}//寫入緩存function setUploadedToStorage(index) {var obj = getUploadedFromStorage();obj[index]=true; localStorage.setItem(saveChunkKey, JSON.stringify(obj) );}//分段對比var uploadedInfo = getUploadedFromStorage();//獲得已上傳的分段信息for(var i=0;i< chunkCount;i++){console.log('index',i, uploadedInfo[i]?'已上傳過':'未上傳');if(uploadedInfo[i]){//對比分段sendChunkCount=i+1;//記錄已上傳的索引continue;//如果已上傳則跳過}var fd = new FormData(); //構造FormData對象fd.append('token', token);fd.append('f1', chunks[i]);fd.append('index', i);(function (index) {xhrSend(fd, function () {sendChunkCount += 1;//將成功信息保存到本地setUploadedToStorage(index);if (sendChunkCount === chunkCount) {console.log('上傳完成,發送合并請求');var formD = new FormData();formD.append('type', 'merge');formD.append('token', token);formD.append('chunkCount', chunkCount);formD.append('filename', name);xhrSend(formD);}});})(i);}

(14) node 端上傳圖片

node

/*** filepath = 相對根目錄的路徑即可*/async function getFileBufer(filePath) => {return new Promise((resolve) => {fs.readFile(filePath, function (err, data) {var bufer = null;if (!err) {resolve({err: err,data: data});}});});}/*** 上傳文件*/let fetch = require('node-fetch');let formData = require('form-data');module.exports = async (options) => {let {imgPath} = options;let data = await getFileBufer(imgPath);if (data.err) {return null;}let form = new formData();form.append('xxx', xxx);form.append('pic', data.data);return fetch('http://xx.com/upload', {body: form,method: 'POST',headers: form.getHeaders()//要活的 form-data的頭,否則無法上傳}).then(res => {return res.json();}).then(data => {return data;})}

(15)其他

JS

var file = document.getElementById('f1').files[0];//判斷類型if(f.type!=='image/jpeg' && f.type !== 'image/jpg' ){alert('只能上傳 jpg 圖片');flag=false;break;}//判斷大小if(file.size>100*1024){alert('不能大于100kb');}//判斷圖片尺寸var img =new Image();img.onload=function(){console.log('圖片原始大小 width*height', this.width, this.height);if(this.naturalWidth){console.log('圖片原始大小 naturalWidth*naturalHeight', this.naturalWidth, this.naturalHeight);}else{console.log('oImg.width*height', this.width, this.height);}}

(16)input file 外觀更改

總結

以上是生活随笔為你收集整理的文件上传攻略的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩黄色精品 | 伊人亚洲天堂 | jizz日本视频 | 女女h百合无遮涩涩漫画软件 | 91精品国产综合久久久久 | av在线资源 | 国产黄色a| 在线观看理论片 | 五月天国产| 亚洲欧美激情另类 | 成人手机av| 91超碰在线观看 | 精品人妻一区二区三区蜜桃 | 岛国av免费在线 | 秋霞电影网一区二区 | 青青青手机在线视频 | 日日做夜夜爽毛片麻豆 | 中文在线а√在线8 | 无人码人妻一区二区三区免费 | 成人黄色一级 | 狠狠操导航 | 欧美福利电影 | 色情毛片| 日韩av不卡一区二区 | 中国肥胖女人真人毛片 | av在线等| 国产乱码精品一区二区三区忘忧草 | 欧美一线高本道 | 91精品啪在线观看国产 | 天天综合久久 | av在线免费网址 | 探花国产 | 美女啪啪免费视频 | 久久黄网| 91亚洲精选 | 拍真实国产伦偷精品 | 亚洲第3页| 爱爱综合网 | 精品人成 | 91美女视频在线观看 | 亚洲色图影院 | 懂色av一区二区三区四区 | 四虎影视永久地址 | 米奇影视第四色 | 国产精品综合久久 | 在线二区| 床戏高潮做进去大尺度视频网站 | 蜜桃传媒一区二区亚洲 | 国产精品乱码 | 大咪咪dvd | 日本老妇性生活 | 污污网站免费在线观看 | 337p色噜噜| 自拍视频网站 | 亚洲色图校园春色 | 性久久久久久久久久久久 | 91麻豆精品国产91久久久久久久久 | 日本裸体网站 | 久久9精品区-无套内射无码 | 中文字幕人成乱码熟女香港 | 就操网 | 日本黄色片| 九色在线观看视频 | 无码免费一区二区三区免费播放 | 久草资源 | av地址在线观看 | 激情六月天婷婷 | 久久一区av | 久久久全国免费视频 | 蜜乳av中文字幕 | 日日夜夜av | 欧美成人做爰大片免费看黄石 | 午夜精品剧场 | 亚洲深夜av | www.久久综合 | 亚欧精品视频一区二区三区 | 国产吞精囗交久久久 | 色88久久久久高潮综合影院 | 少妇把腿扒开让我舔18 | 中文字幕在线视频一区二区三区 | 国产秋霞 | 先锋影音av资源在线 | 丰满少妇一区二区三区专区 | 久久精品—区二区三区舞蹈 | 无人在线观看高清视频 | 中文字幕一区二区三区日韩精品 | 依依成人综合 | 国产成人精品一区 | 国产福利午夜 | 天天躁日日躁狠狠躁欧美 | 国产春色| 久久99精品久久久久 | 91欧美一区二区 | 超碰在线中文字幕 | 国模无码大尺度一区二区三区 | 男人操女人免费视频 | 曰女同女同中文字幕 | 激情文学综合网 | 色综网|