原生js极简文件选择器,文件数量大小类型限制,元素自动渲染,自动获取formData提交至后端

以前对文件处理一直很抵触,因为练习成本较高,原生的文件选择器了解不多,所以最近补了补课,然后自己手搓了一个文件选择器,先看看效果。

 基础样式就是这样子,然后直接把文件拖入或者点击选择文件就行

图片文件支持预览,可以删除已选中文件,文件上传可以限制数量

引用的方法如下

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="FileUploader"></div>
	</body>
</html>
<script src="js/Fileuploader.js"></script>
<script>
	new FileUploader({
		id: "FileUploader",
		limit: 10,
		fileSize: [{
			type: "*",
			size: "10MB",
		}],
		url: "http://localhost:7385/fileUpload"
	}, this)
</script>

 new一个对象就行,id绑定一个div,然后样式会自动注入,

limit是限制上传数量,

fileSize数组里插入文件限制的大小,“ * ”是全部,支持对单种文件类型限制大小,输入Mb、B、kb、Gb,或者数字都行,自动转成字节大小,比如:

new FileUploader({
		id: "FileUploader",
		limit: 10,
		fileSize: [{
			type: "png",
			size: "500KB",
		}, {
			type: "JGP",
			size: "151515",
		}, {
			type: "docx",
			size: "1Mb",
		}],
		url: "http://localhost:7385/fileUpload"
	}, this)

url是提交到的接口,使用内置的方法会发送一个formdata,用List<MultipartFile>接收使用方法如下:

    let fileUpload=new FileUploader({
		id: "FileUploader",
		limit: 10,
		fileSize: [{
			type: "*",
			size: "10MB",
		}],
		url: "http://localhost:7385/fileUpload"
	}, this)
    
    //运行这一句就行
    fileUpload.submit()

但是不同的项目需求可能不太一样,所以还是推荐直接获取formData然后自己上传,方法如下:


    let fileUploader=new FileUploader({
		id: "FileUploader",
		limit: 10,
		fileSize: [{
			type: "*",
			size: "10MB",
		}],
		url: "http://localhost:7385/fileUpload"
	}, this)
    
    var formData=fileUploader.getFormData()

这玩意写了几个小时,考虑的东西还不太全面,代码样式比较难改,因为我想把玩意封装到一个js里,所以css也是js注入,具体代码如下,自取:

class FileUploader {
	constructor(setting, vue) {

		this.setting = setting
		this.Vue = vue
		this.fileSizeLimit = null
		if (this.setting.fileSize != null) {
			this.fileSizeLimit = new Map();
			for (var a = 0; a < this.setting.fileSize.length; a++) {
				//console.log(this.setting.fileSize[a])
				this.fileSizeLimit.set(this.setting.fileSize[a].type, this.convertToBytes(this.setting.fileSize[a]
					.size))
			}
		}
		//console.log(this.fileSizeLimit)
		this.styleNum = 0
		this.fileList = []
		this.fileUpload = document.getElementById(this.setting.id)
		console.log(this.Vue)
		this.fileUpload.classList.add("FILEUPLOADERfileBaseUploaderBlock")
		this.styleElement = document.createElement('style');
		document.head.appendChild(this.styleElement);
		this.styleSheet = this.styleElement.sheet;
		this.addCss()
		this.init()
		this.inputDisable()
		this.fileListEle = this.dc("div", "FILEUPLOADERfileListEle")
		this.fileUpload.append(this.fileListEle)
	}
	convertToBytes(size) {
		const units = {
			B: 1,
			KB: 1024,
			MB: 1024 * 1024,
			GB: 1024 * 1024 * 1024,
			TB: 1024 * 1024 * 1024 * 1024,
		};
		const regex = /^(\d+(\.\d+)?)\s*([BKMGT]B?)$/i;
		const matches = size.match(regex);

		if (matches) {
			const value = parseFloat(matches[1]);
			const unit = matches[3].toUpperCase();

			if (units.hasOwnProperty(unit)) {
				return value * units[unit];
			}
		} else if (!isNaN(size)) {
			// 输入的是纯数字,直接返回大小
			return parseFloat(size);
		}
		return NaN;
	}
	getFormData() {
		const formData = new FormData();

		// 将文件数组添加到 FormData 中
		for (let i = 0; i < this.fileList.length; i++) {
			formData.append('files', this.fileList[i]);
		}
		return formData
	}
	async submit() {
		if (this.fileList.length == 0) {
			this.message("未选择文件")
			return
		}
		// 创建一个 FormData 对象
		const formData = new FormData();

		// 将文件数组添加到 FormData 中
		for (let i = 0; i < this.fileList.length; i++) {
			formData.append('files', this.fileList[i]);
		}

		const {
			data: result
		} = await this.Vue.$http.post(this.setting.url, formData)
		this.message("上传成功")
		if (result.code == 200) {
			this.fileListEle.innerHTML = ''
			this.fileList = []
			return result
		} else {
			return result
		}
		// 发起 POST 请求到后端接口
		// fetch('http://123.56.27.115:7385/fileUpload', {
		// 		method: 'POST',
		// 		body: formData,
		// 	})
		// 	.then(response => {
		// 		//console.log(response)
		// 	})
		// 	.catch(error => {
		// 		//console.error('请求错误:', error);
		// 	});
	}
	message(msg) {
		var Msg = this.dc("div", "FILEUPLOADERmessage").text(msg)
		document.body.appendChild(Msg)
		var op = 0
		var top = 0
		var emo = self.setInterval(() => {
			op += 0.1
			Msg.style.opacity = op;
			top = top + (1.5 - (top / 7 * top / 7));
			//console.log((1.5 - (top / 7 * top / 7)))
			Msg.style.top = top + "%";
			//console.log(top)
		}, 17)
		setTimeout(() => {
			self.clearInterval(emo)

		}, 250)
		setTimeout(() => {

			document.body.removeChild(Msg)
		}, 2000)
	}
	viewFileList() {
		if (this.fileList[this.fileList.length - 1].type.substr(0, 5) == "image") {
			const reader = new FileReader();
			reader.readAsDataURL(this.fileList[this.fileList.length - 1]);
			//console.log(this.fileList[this.fileList.length - 1])
			reader.onload = (event) => {
				const img = new Image();
				img.src = event.target.result;
				img.onload = () => {
					let delIcon = this.dc("div", "FILEUPLOADERdelIcon")
					let index = this.fileList.length - 1
					const fileViewBlock = this.dc("div", "FILEUPLOADERfileViewBlock")
					const canvas = this.dc("canvas", "FILEUPLOADERviewCanvas");
					fileViewBlock.append(delIcon).append(this.dc("div", "FILEUPLOADERviewCanvasBlock")
							.append(canvas))
						.append(this.dc(
								"div", "FILEUPLOADERfileNameImg").text(this.fileList[this.fileList.length -
								1].name)
							.tit(
								this.fileList[this.fileList.length - 1].name))
					this.fileListEle.append(fileViewBlock)
					delIcon.onclick = () => {
						this.fileList.splice(index, 1)
						this.fileListEle.removeChild(fileViewBlock)
						this.inputAble()
						//console.log(this.fileList)
					}
					const ctx = canvas.getContext('2d');
					canvas.width = img.width;
					canvas.height = img.height;
					ctx.drawImage(img, 0, 0);
					const width = img.width;
					const height = img.height;
					ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
				};
				img.onerror = (error) => {
					reject(error);
				};
			};
		} else {
			let index = this.fileList.length - 1

			let delIcon = this.dc("div", "FILEUPLOADERdelIcon")
			const fileViewBlock = this.dc("div", "FILEUPLOADERfileViewBlock").append(delIcon).append(this.dc("div",
				"FILEUPLOADERfile-icon").append(this
				.dc("div", "FILEUPLOADERfileName").text(this.fileList[this.fileList.length - 1].name).tit(
					this
					.fileList[this.fileList.length - 1].name)))
			this.fileListEle.append(fileViewBlock)
			delIcon.onclick = () => {

				this.fileList.splice(index, 1)
				this.fileListEle.removeChild(fileViewBlock)
				this.inputAble()
				//console.log(this.fileList)
			}
		}
	}

	init() {
		this.fileUpload.classList.add("FILEUPLOADERuploadInput")
		this.fileInputBlock = this.dc("div", "FILEUPLOADERinputBlock")
		this.fileInput = this.dc("input", "FILEUPLOADERInput")
		this.fileInput.type = "file"
		this.fileInput.addEventListener('change', (event) => {
			if (this.fileInput.disabled) {
				this.message("已达到限制上传数量")
				return
			}
			for (var a = 0; a < this.fileList.length; a++) {
				if (this.fileList[a].name == event.target.files[0].name && this.fileList[a].lastModified ==
					event
					.target.files[0].lastModified && this.fileList[a].size == event.target.files[0].size) {
					this.message("请勿重复上传文件")
					return
				}
			}
			var fileType = event.target.files[0].name.split(".")
			var fileType = fileType[fileType.length - 1];
			if (this.fileSizeLimit.has("*")) {
				if (this.fileSizeLimit.get("*") < event.target.files[0].size) {
					this.message("超过限制大小(" + this.formatBytes(this.fileSizeLimit.get("*")) + ")")
					return
				}
			} else if (this.fileSizeLimit.has(fileType)) {
				if (this.fileSizeLimit.get(fileType) < event.target.files[0].size) {
					this.message("超过限制大小(" + this.formatBytes(this.fileSizeLimit.get(fileType)) + ")")
					return
				}
			}

			this.fileList.push(event.target.files[0])
			this.fileNumEle.text(this.setting.limit + "/" + this.fileList.length)
			this.inputDisable()
			//console.log(this.fileList)
			this.viewFileList()
		});
		this.signFont = this.dc("font", "FILEUPLOADERaddFont").text("点击/拖拽选择")
		this.addIcon = this.dc("font", "FILEUPLOADERaddIcon").text("+")
		this.fileNumEle = this.dc("div", "FILEUPLOADERfileNum").text(this.setting.limit + "/0")
		this.fileInputBlock.append(this.fileInput).append(this.dc("div", "FILEUPLOADERinputIcon").append(this
			.addIcon).append(this.signFont))
		this.fileUpload.append(this.fileInputBlock)
		this.fileUpload.append(this.fileNumEle)
		this.fileInputBlock.addEventListener('mouseover', (event) => {
			this.fileInputBlock.style.opacity = "0.6"
		});
		this.fileInputBlock.addEventListener('mouseout', (event) => {
			this.fileInputBlock.style.opacity = "1"
		});
		this.fileInputBlock.addEventListener('dragover', (event) => {
			this.fileInputBlock.style.opacity = "0.6"
		});
		this.fileInputBlock.addEventListener('dragleave', (event) => {
			this.fileInputBlock.style.opacity = "1"
		});
		this.fileInputBlock.addEventListener('drop', (event) => {
			this.fileInputBlock.style.opacity = "1"
		});
		this.fileInputBlock.addEventListener('change', (event) => {
			this.fileInputBlock.style.opacity = "1"
		});

	}
	formatBytes(bytes) {
		if (bytes === 0) {
			return '0 B';
		}

		const k = 1024;
		const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

		const i = Math.floor(Math.log(bytes) / Math.log(k));
		const size = parseFloat((bytes / Math.pow(k, i)).toFixed(2));

		return size + ' ' + sizes[i];
	}
	inputDisable() {
		if (this.fileList.length == this.setting.limit) {
			this.fileInput.disabled = true;
			this.fileInputBlock.style.border = "2px solid #9e9e9e"
			this.signFont.style.color = "#9e9e9e"
			this.addIcon.style.color = "#9e9e9e"
		}
	}
	inputAble() {
		//console.log(this.fileList.length + "," + this.setting.limit)
		if (this.fileList.length != this.setting.limit) {
			this.fileInput.disabled = false;
			this.fileInputBlock.style.border = "2px solid #599cff"
			this.signFont.style.color = "#599cff"
			this.addIcon.style.color = "#599cff"
		}
	}
	addCss() {
		// this.style(`.message{width:20%;padding:15px;margin-left:40%;}`)
		this.style(`.FILEUPLOADERfileViewBlock{width:75px;height:95px;float:left;margin:5px;overflow:hidden;}`)
		this.style(`.FILEUPLOADERfileBaseUploaderBlock {
				display: flex;
				width:98%;
				flex-direction: column;
				float: left;
				padding:1%;
				background-color:#fff;
			}`)
		this.style(`.FILEUPLOADERuploadInput{padding:10px;display:flex;flex-direction:column;width:100%}`)

		this.style(
			`.FILEUPLOADERInput{height:100%;width:100%; margin-top:-0px; float:left;background-color:red;opacity: 0;cursor: pointer;}`
		)
		this.style(`.FILEUPLOADERfileNum{float:left;margin-top:-30px;margin-left:115px}`)
		this.style(
			`.FILEUPLOADERfileName{background-color:#f8f8f8;width:71px;padding-left:2px;padding-right:2px; height:25px;text-align:center;line-height:20px;float:left;font-size: 12px;white-space: nowrap;overflow:hidden;margin-top:75px;}`
		)

		this.style(
			`.FILEUPLOADERfileNameImg{background-color:#f8f8f8;width:71px;padding-left:2px;padding-right:2px; height:25px;text-align:center;line-height:20px;float:left;font-size: 12px;white-space: nowrap;overflow:hidden;}`
		)

		this.style(
			`.FILEUPLOADERviewCanvasBlock{width:75px;height:75px;overflow:hidden;float:left;background-color:#f8f8f8;}`
		)

		this.style(`.FILEUPLOADERviewCanvas{width:75px; position: relative;
				z-index: 1;}`)

		this.style(
			`.FILEUPLOADERinputBlock{width:100px;height:100px;float:left;border:2px solid #33a0ff;border-radius:3px;cursor: pointer;}`
		)

		this.style(
			`.FILEUPLOADERinputIcon {width: 100% ;height: 100% ;float: left;margin-top: -100% ;color: #599cff;}`
		)

		this.style(`.FILEUPLOADERfile-icon {background-color:#f8f8f8;float:left;
				width: 75px;
				height: 75px;
				background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024' width='75' height='75'%3E%3Cpath d='M506.88 257.024H321.024c-25.6 0-46.08 20.992-46.08 46.592v371.2c0 25.6 20.48 46.592 46.08 46.592h279.04c25.6 0 46.592-20.992 46.592-46.592V396.288L506.88 257.024z m46.08 371.2H367.616v-46.592H552.96v46.592z m0-92.672H367.616v-46.592H552.96v46.592zM483.328 419.328V291.84l127.488 127.488H483.328z' fill='%23AAD4FF'/%3E%3Cpath d='M599.552 314.88H413.696c-25.6 0-46.08 20.992-46.08 46.592v371.2c0 25.6 20.48 46.592 46.08 46.592h279.04c25.6 0 46.592-20.992 46.592-46.592V454.144l-139.776-139.264z m46.592 371.2H460.288v-46.08h185.856v46.08z m0-92.672H460.288v-46.592h185.856v46.592z m-69.632-116.224V349.696l127.488 127.488h-127.488z' fill='%232B95FF'/%3E%3Cpath d='M827.904 233.472H798.72v-29.696c0-6.656-5.12-11.776-11.776-11.776-6.656 0-11.776 5.12-11.776 11.776v29.696h-29.696c-6.656 0-11.776 5.12-11.776 11.776s5.12 11.776 11.776 11.776h29.696V286.72c0 6.656 5.12 11.776 11.776 11.776 6.656 0 11.776-5.12 11.776-11.776v-29.696h29.696c6.656 0 11.776-5.12 11.776-11.776s-5.632-11.776-12.288-11.776z' fill='%23A8D4FF'/%3E%3Cpath d='M805.66272 620.4416l46.336-46.336 46.34624 46.336-46.34112 46.336zM786.944 831.488l34.816-34.816h-69.632z' fill='%23D9EDFF'/%3E%3Cpath d='M216.064 234.496m-32.768 0a32.768 32.768 0 1 0 65.536 0 32.768 32.768 0 1 0-65.536 0Z' fill='%23D9EDFF'/%3E%3Cpath d='M281.088 794.112l-89.088-89.088c-3.072-3.072-8.192-4.096-12.8-2.56-4.096 2.048-7.168 6.144-7.168 10.752v89.088c0 3.072 1.536 6.144 3.584 8.192 2.048 2.048 5.12 3.584 8.192 3.584h89.088c4.608 0 8.704-3.072 10.752-7.168 1.536-4.096 0.512-9.216-2.56-12.8z m-86.016-3.072v-49.152l49.152 49.152h-49.152z' fill='%23A8D4FF'/%3E%3C/svg%3E");
				background-repeat: no-repeat;
				background-position: center;
				}
				`)
		this.style(`.FILEUPLOADERmessage {
				opacity:0;
				position: fixed;
				top: 0%;
				border-radius:3px;
				text-align: center;
				min-width: 200px;
				left: 50%;
				font-size: 14px;
				background-color: #c2c2c2;
				padding: 5px;
				box-shadow: 0 0px 3px #c2c2c2;
				z-index: 9999;
				color: #ffffff;
			}`)
		this.style(`.FILEUPLOADERdelIcon {cursor: pointer;
				margin-left:50px;
				background-color:#fff;
				border-radius:50%;
				margin-bottom:-25px;
				position: relative;
				z-index: 10;
				width: 20px;
				height: 20px;
				background-image: url("data:image/svg+xml,%3Csvg t='1686553471493' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3537' width='20' height='20'%3E%3Cpath d='M872.802928 755.99406 872.864326 755.99406 872.864326 755.624646Z' fill='%23272536' p-id='3538'%3E%3C/path%3E%3Cpath d='M927.846568 511.997953c0-229.315756-186.567139-415.839917-415.838893-415.839917-229.329059 0-415.85322 186.524161-415.85322 415.839917 0 229.300406 186.524161 415.84094 415.85322 415.84094C741.278405 927.838893 927.846568 741.29836 927.846568 511.997953M512.007675 868.171955c-196.375529 0-356.172979-159.827125-356.172979-356.174002 0-196.374506 159.797449-356.157629 356.172979-356.157629 196.34483 0 356.144326 159.783123 356.144326 356.157629C868.152001 708.34483 708.352505 868.171955 512.007675 868.171955' fill='%23272536' p-id='3539'%3E%3C/path%3E%3Cpath d='M682.378947 642.227993 553.797453 513.264806 682.261267 386.229528c11.661597-11.514241 11.749602-30.332842 0.234337-41.995463-11.514241-11.676947-30.362518-11.765975-42.026162-0.222057L511.888971 471.195665 385.223107 344.130711c-11.602246-11.603269-30.393217-11.661597-42.025139-0.059352-11.603269 11.618619-11.603269 30.407544-0.059352 42.011836l126.518508 126.887922L342.137823 639.104863c-11.662621 11.543917-11.780301 30.305213-0.23536 41.96988 5.830799 5.89015 13.429871 8.833179 21.086248 8.833179 7.53972 0 15.136745-2.8847 20.910239-8.569166l127.695311-126.311801L640.293433 684.195827c5.802146 5.8001 13.428847 8.717546 21.056572 8.717546 7.599072 0 15.165398-2.917446 20.968567-8.659217C693.922864 672.681586 693.950494 653.889591 682.378947 642.227993' fill='%23272536' p-id='3540'%3E%3C/path%3E%3C/svg%3E");
				}
				`)
		this.style(`.FILEUPLOADERdelIcon:hover{background-color:#e5e5e5;}`)
		this.style(
			`.FILEUPLOADERaddIcon {width: 100%;text-align: center;font-size: 50px;float: left;}`
		)

		this.style(
			`.FILEUPLOADERaddFont {margin-top: -5px;font-size: 13px;float: left;width: 100%;text-align: center;}`
		)

		this.style(`.FILEUPLOADERfileListEle {background-color:#f0f0f0;width:570px;margin-top:15px;}`)
	}

	style(css) {
		// 插入 CSS 样式规则到 StyleSheet 中
		this.styleSheet.insertRule(css, this.styleNum);
		this.styleNum++
	}
	dc(element, Class, valueBody) {

		var Element = document.createElement(element)
		if (Class != "") {
			try {
				Element.classList.add(Class)
			} catch (e) {
				//TODO handle the exception
			}
		}
		Element["text"] = function(text) {
			Element.innerText = text
			return Element
		}
		Element["addClass"] = function(newClass) {
			Element.classList.add(newClass)
			return Element
		}
		Element["append"] = function(child) {
			Element.appendChild(child)
			return Element
		}
		Element["tit"] = function(title) {
			Element.title = title
			return Element
		}
		valueBody = Element
		return Element
	}
}

问题肯定很多,代码写法肯定问题也有,如果有兴趣可以自己优化一下,毕竟是第一版。

叠个甲,如果你说elementui或者layui也有文件选择器,那么你说得对,我用过,但是我觉得那个耦合太高了,所以我就想只写一个文件选择器,并且能把前端代码量降到最小,全封装到一起,这样方便应该是比自己手搓方便,耦合度也低,但是就是功能不够强大,在js里写前端样式也比较逆天,所以图个乐,小练习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值