Hi,大家好,昨天我們加入了資料驗證機制的設定後,輸入表單已經是有了初步的雛形了,今天我們要來加強表單的功能。多數在做問題反應的機制上,一定會加上檔案上傳的功能,以方便實際上處理問題的人有參考資料,今天我們就來把圖片上傳功能加進去吧
在html中,要做到檔案上傳時,100%會用到的東西是input中的 file 物件,但是上傳的做法就有數種了,第一種是最傳統的,以form post 來做上傳動作
<form action="upload.php" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit" value="Upload Image" name="submit">
</form>
說明:這是最傳統的做法,準備表單並指定method="post" 與 enctype="multipart/form-data" ,表單送出後就完成上傳動作
第二種是透過 ajax 進行上傳
const app = new Vue({
data: () => ({images: null}),
template: `
<div>
<input type="file" @change="uploadFile" ref="file">
<button @click="submitFile">Upload!</button>
</div>
`,
methods: {
uploadFile() {
this.Images = this.$refs.file.files[0];
},
submitFile() {
const formData = new FormData();
formData.append('file', this.Images);
const headers = { 'Content-Type': 'multipart/form-data' };
axios.post('https://httpbin.org/post', formData, { headers }).then((res) => {
res.data.files; // binary representation of the file
res.status; // HTTP status
});
}
}
});
app.$mount("#content");
說明:第二種做法其實是第一種做法的 vue.js 版本,將資料轉換成為 FormData ,並在表單中加入 { 'Content-Type': 'multipart/form-data' } 的 header 後透過 axios 送出。
上述2種方法都是可用的,也是常見的做法。不過今天我們來點不一樣的,透過 fileReader 將檔案轉成 dataURL 後,把檔案當成普通文字資料上傳,那麼我們開始吧
dataURL 可以理解成為是一個資料傳輸的格式,是由WHATWG 制訂出來的,它主要的資料結構是「data:[][;base64], 」,例如: data:text/plain;base64,SGVsbG8sIFdvcmxkIQ== ,它最方便的地方就是因為已經被轉換成 BASE64編碼資料了,所以傳輸的方式很簡單,當一般文字傳輸就行了。另外還有一個方便的地方是如果資料內容是圖檔的話,將 dataURL 的內文指定給 img 的 src 屬性,瀏覽器會自動把圖檔打開來顯示,可以做到前端預覽圖檔的效果。
那麼,今天的程式碼如下
<style>
.formtitle::before {
color: red;
content: "*";
}
.err {
color: red;
background-color: pink;
}
</style>
<div id="app">
<div class="d-flex align-items-center py-4 bg-body-tertiary">
<main class="form-signin w-100 m-auto">
<h1 class="h3 mb-3 fw-normal">請輸入您的問題</h1>
<div class="form-floating">
<input type="text" v-model="data.casedesc" :class="{ err: data.inErr }" class="form-control" id="floatingInput">
<label for="floatingInput" class="formtitle" :class="{ err: data.inErr }" > 問題概述</label>
</div>
<label for="floatingPassword" class="formtitle" > 詳細說明</label>
<div class="form-floating">
<textarea v-model="data.caseinfo" id="floatingPassword" style="width: 100%;" rows="20" :class="{ err: data.inErr }">
</textarea>
</div>
<div class="form-floating">
<input type="file" class="form-control" id="fileInput" accept="image/*" @change="readfile">
<label for="fileInput">附件上傳</label>
<img v-bind:src="data.fileinfo"/>
</div>
<button class="btn btn-primary w-100 py-2" @click="senddata()">送出</button>
</main>
</div>
</div>
<script>
const { createApp, onBeforeMount, reactive } = Vue
let data = reactive({ casedesc: "", caseinfo: "", inErr: false})
createApp({
setup() {
return { data }
},
methods: {
readfile(e) {
let file = e.target.files[0]
let reader = new FileReader()
if(file) {
reader.onload = function(e) {
data.fileinfo = reader.result;
}
reader.readAsDataURL(file)
}
},
senddata() {
if(data.casedesc === "" || data.caseinfo === "") {
alert("請務必輸入問題描述與說明")
data.inErr = true
return false
}
axios.post("/saf/mgr/api/saveCase", {
casedesc: data.casedesc,
caseinfo: data.caseinfo,
fileinfo: data.fileinfo
}).then(function (response) {
let rt = response.data
if (rt.status == "00") {
alert("存檔成功")
}
}).catch(function (response) {
alert(response.response.data)
})
}
}
})
.mount('#app')
</script>
說明:
將 dataURL 轉成檔案也是很簡單,我們只要用簡單的文字處理方式就可以做到
router.post("/api/saveCase", async (req, res, next) => {
//讀取表單內容
let fileinfo = req.body.fileinfo
let regex = /^data:.+\/(.+);base64,(.*)$/;
//以 re 過慮文字後,轉為資料陣列,其中陣列的最後一項即為以 BASE64 編碼的檔案資料
let matches = fileinfo.match(regex);
let ext = matches[1];
let data = matches[2];
//BASE64 轉為 buffer 後寫入檔案即可
let buffer = Buffer.from(data, 'base64');
fs.writeFileSync(path.join(__dirname, "file" + ext, buffer))
//...
})
說明:簡單的說,就是把文字切開後,截取我們要的部份進行 base64 解碼後並存成檔案即可
今天完成了圖檔上傳的功能,可以看出來,以dataURL 的檔案上傳其實就是這麼簡單,但是它有個缺點是不會夾帶檔案資訊上去,如同我存檔的程式碼所顯示的狀況一樣,系統只知道這是什麼檔案類型而已,若是存檔時需要保留原始檔名,或是需要過取得檔案的建立日期等資訊,dataURL 可能就不適用了。今天完成了檔案上傳的功能之後,我們明天要來進行圖檔檢視功能的製作,那麼,我們明天再繼續吧