由于文件安全问题,要求上传文件的时候,把文件流加密后传递给后端,且下载的时候,解密后,方可下载文件。
有两种方法、一种将文件转base64加密 一种是转二进制数据加密、由于base64会将原文件大小变大很多倍,二进制不会、因此推荐使用方法二。
以下是相关代码:
方法一
1、引入crypto-js
并封装加密、解密的方法,具体代码如下:
// utils/jsencrypt.js import cryptojs from 'crypto-js'; // des加密 export const encryptbydes = (message, key) => { var keyhex = cryptojs.enc.utf8.parse(key); var encrypted = cryptojs.aes.encrypt(message, keyhex, { mode: cryptojs.mode.ecb, padding: cryptojs.pad.pkcs7 }); return encrypted.tostring(); } // des解密 export const decryptbydes = (ciphertext, key) => { var keyhex = cryptojs.enc.utf8.parse(key); // direct decrypt ciphertext var decrypted = cryptojs.aes.decrypt({ ciphertext: cryptojs.enc.base64.parse(ciphertext) }, keyhex, { mode: cryptojs.mode.ecb, padding: cryptojs.pad.pkcs7 }); return decrypted.tostring(cryptojs.enc.utf8); }
2、文件上传时加密
页面:
<el-upload action="#" :before-upload="handlebeforeupload" :file-list="filelist" :limit="1" :on-error="handleuploaderror" :on-exceed="handleexceed" :on-success="handleuploadsuccess" :show-file-list="false" :headers="headers" :http-request="handlefileupload" class="upload-file-uploader" ref="upload" > <!-- 上传按钮 --> <el-button size="mini" type="primary" :loading="loading">选取文件</el-button> <!-- 上传提示 --> <div class="el-upload__tip" slot="tip" v-if="showtip"> 请上传 <template v-if="filesize"> 大小不超过 <b style="color: #f56c6c">{{ filesize }}mb</b> </template> <template v-if="filetype"> 格式为 <b style="color: #f56c6c">{{ filetype.join("/") }}</b> </template> 的文件 </div> </el-upload>
上传文件(加密文件):
首先使用filereader中readasdataurl将上传的文件流转成base64字符串,然后使用加密事件将base64字符串加密,传递给后端。
// 上传文件 handlefileupload(file){ this.loading = true let reader = new filereader() reader.readasdataurl(file.file) // 读取base64 reader.onload = ()=>{ // reader.result 读取base64 // 使用 aes 算法对 reader 进行加密 let encrypted = encryptbydes(reader.result, 'abcdefghijklmno12345678910234567') // 此加密key可自定义 this.base64string = encrypted // 获取文件类型 this.filetypes = file.file.name.substring(file.file.name.lastindexof(".")) let params = { filename:file.file.name, filetype:this.filetypes, filesize:file.file.size, base64:this.base64string // 将加密后的文件字符串传给后端 } getfileupload(params).then((res)=>{ this.loading = false this.$message.success("上传成功"); this.filename=res.filename console.log(this.filename,'this.filename') this.$emit("input", res.fileid); this.$emit('filename',this.filename) }).catch((error)=>{ this.loading = false this.$message.error("上传失败, 请重试"); console.log(error,'error') this.filelist = [] this.list = [] }) } },
下载文件(解密文件):
先将获取到的加密串解密,然后将base64串转成blob格式,然后下载文件,具体代码如下:
// 通过接口获取到加密字符串files keyfiles(params).then((files)=>{ // 将加密字符串files解密 let base64url = decryptbydes(files,'abcdefghijklmno12345678910234567') // 将解密到的base64字符串转成blob格式 let blob = this.base64toblob(base64url) // 下载文件 this.blobdownload(blob,this.listparams.filename) this.$set(row, 'loading', false) })
base64toblob(datauri) { // 将 datauri 分割为类型和数据两部分 var parts = datauri.split(","); var type = parts[0].match(/:(.*?);/)[1]; var data = parts[1]; // 将 base64 数据解码为二进制数据 var bytestring = atob(data); // 将二进制数据转换为类型化数组 var arraybuffer = new arraybuffer(bytestring.length); var intarray = new uint8array(arraybuffer); for (var i = 0; i < bytestring.length; i++) { intarray[i] = bytestring.charcodeat(i); } // 创建一个 blob 对象 var blob = new blob([intarray], { type: type }); return blob; },
blobdownload(blob,filename){ // 判断浏览器类型 if (window.navigator && window.navigator.mssaveoropenblob) { // 如果是 ie 浏览器,使用 mssaveoropenblob 方法 window.navigator.mssaveoropenblob(blob, filename); } else { var myurl = url.createobjecturl(blob); //创建图片的临时url // downloadfile(myurl,name) var a = document.createelement("a") //新建一个a链接 a.setattribute("href",myurl) // a链接的url为图片的url a.setattribute("download",filename) a.setattribute("target","_blank") let clickevent = document.createevent("mouseevents"); clickevent.initevent("click", true, true); a.dispatchevent(clickevent); } },
由于要支持ie浏览器,注意crypto-js的版本,一开始使用4.1.1 不支持ie,后面改成4.0.0即可。
方法二
由于将文件转成base64加密后使文件大小比之前大了很多倍,存到服务器很不友好,于是使用二进制进行加解密、保持与之前大小一致,具体如下:
1、封装加密、解密的方法
import cryptojs from 'crypto-js'; import cryptou8array from "./crypto-en" var key = cryptojs.enc.utf8.parse("1111111111111111"); //十六位十六进制数作为秘钥 var iv = cryptojs.enc.utf8.parse('1111111111111111');//十六位十六进制数作为秘钥偏移量 // 使用 aes 进行文件加密 export function encryptfile(word) { const messagewordarray = cryptou8array.u8array.parse(word); var encrypted = cryptojs.aes.encrypt(messagewordarray, key, { iv: iv, mode: cryptojs.mode.cbc, padding: cryptojs.pad.pkcs7 }); let encryptedbytes = encrypted.ciphertext return cryptou8array.u8array.stringify(encryptedbytes) } // 使用 aes 进行文件解密 export function decryptfile(word) { const messagewordarray = cryptou8array.u8array.parse(word); var dcbase64string = messagewordarray.tostring(cryptojs.enc.base64); var decrypt = cryptojs.aes.decrypt(dcbase64string, key, { iv: iv, mode: cryptojs.mode.cbc, padding: cryptojs.pad.pkcs7 }); // var decryptedstr = decrypt.tostring(cryptojs.enc.utf8); return cryptou8array.u8array.stringify(decrypt) }
// crypto-en.js import cryptojs from 'crypto-js' cryptojs.enc.u8array = { /** * converts a word array to a uint8array. * * @param {wordarray} wordarray the word array. * * @return {uint8array} the uint8array. * * @static * * @example * * var u8arr = cryptojs.enc.u8array.stringify(wordarray); */ stringify: function (wordarray) { // shortcuts var words = wordarray.words var sigbytes = wordarray.sigbytes // convert var u8 = new uint8array(sigbytes) for (var i = 0; i < sigbytes; i++) { var byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff u8[i] = byte } return u8 }, /** * converts a uint8array to a word array. * * @param {string} u8str the uint8array. * * @return {wordarray} the word array. * * @static * * @example * * var wordarray = cryptojs.enc.u8array.parse(u8arr); */ parse: function (u8arr) { // shortcut var len = u8arr.length // convert var words = [] for (var i = 0; i < len; i++) { words[i >>> 2] |= (u8arr[i] & 0xff) << (24 - (i % 4) * 8) } return cryptojs.lib.wordarray.create(words, len) } } export default { u8array: cryptojs.enc.u8array }
2、文件上传时加密
// 上传文件 handlefileupload(file) { this.upload.isuploading = true let reader = new filereader() reader.readasarraybuffer(file.file) // 以二进制数据读取文件 reader.onload = () => { var filecontent = new uint8array(reader.result) // 转成uint8array类型 var encryptedcontent = encryptfile(filecontent); // 加密 console.log(encryptedcontent, '加密后串'); let blobtype = file.file.type let filename = file.file.name let blob = this.atobtoblob(encryptedcontent, blobtype) // 将加密后串转blob let blobtofile = new file([blob], filename, { type: blobtype, lastmodified: date.now() }); // 得到加密文件 let formdata = new formdata(); formdata.append("file", blobtofile); // 传给后端即可 } }, atobtoblob(intarray,type) { // 创建一个 blob 对象 var blob = new blob([intarray], { type: type }); return blob; }
3、解密并下载
// 解密逻辑 file-二进制-解密-blob-file desfilelist() { this.files.map((item) => { let readers = new filereader() readers.readasarraybuffer(item.url) // item.url 为加密后的串 readers.onload = () => { var filecontent = new uint8array(readers.result) // console.log(filecontent, 'filecontent加密前'); var encryptedcontent = decryptfile(filecontent); console.log(encryptedcontent, '解密后3333'); let blob = atobtoblob(encryptedcontent, item.url.type) console.log(blob,'blob'); let blobtofile = new file([blob], item.url.name, { type: item.url.type, lastmodified: date.now() }) item.url = blobtofile item.name = item.url.name item.tag=1 // blobdownload(blob,blobtofile.name) } }) console.log(this.files,'this.files'); settimeout(() => { this.$refs.files.setfilelist(this.files) }, 500); }, export function blobdownload(blob, filename) { // 判断浏览器类型 if (window.navigator && window.navigator.mssaveoropenblob) { // 如果是 ie 浏览器,使用 mssaveoropenblob 方法 window.navigator.mssaveoropenblob(blob, filename); } else { var myurl = url.createobjecturl(blob); //创建图片的临时url // downloadfile(myurl,name) var a = document.createelement("a") //新建一个a链接 a.setattribute("href", myurl) // a链接的url为图片的url a.setattribute("download", filename) a.setattribute("target", "_blank") let clickevent = document.createevent("mouseevents"); clickevent.initevent("click", true, true); a.dispatchevent(clickevent); } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论