效果:
缘由:
<template><div class="upload-container"><div class="upload-box" @click="triggerFileInput" @dragover.prevent @drop="handleDrop"><p>点击或拖放文件到这里上传</p><input type="file" ref="fileInput" @change="handleFileChange" accept=".kml,.shp" hidden /></div><div v-if="file" class="file-info"><p>文件名:{{ file.name }}</p><p>文件大小:{{ formatSize(file.size) }}</p><button @click="uploadFile">上传文件</button></div></div>
</template><script setup>
import { ref } from 'vue';const file = ref(null);
const fileInput = ref(null);// 文件大小格式化
const formatSize = (size) => {return size < 1024 ? `${size} B` :size < 1048576 ? `${(size / 1024).toFixed(1)} KB` :`${(size / 1048576).toFixed(1)} MB`;
};// 触发文件选择
const triggerFileInput = () => {fileInput.value.click();
};// 处理文件选择
const handleFileChange = (event) => {const selectedFile = event.target.files[0];validateFile(selectedFile);
};// 处理文件拖放
const handleDrop = (event) => {const droppedFile = event.dataTransfer.files[0];validateFile(droppedFile);
};// 文件验证函数
const validateFile = (selectedFile) => {if (selectedFile && (selectedFile.name.endsWith('.kml') || selectedFile.name.endsWith('.shp'))) {file.value = selectedFile;} else {alert("请上传 .kml 或 .shp 文件");}
};// 上传文件
const uploadFile = () => {if (!file.value) return;const formData = new FormData();formData.append('file', file.value);fetch('/upload', {method: 'POST',body: formData,}).then(response => response.json()).then(data => {alert("文件上传成功");resetFile();}).catch(error => {alert("上传失败,请重试");});
};// 重置文件信息
const resetFile = () => {file.value = null;fileInput.value.value = ''; // 重置 input 文件值
};
</script><style scoped>
.upload-container {display: flex;flex-direction: column;align-items: center;margin-top: 20px;
}.upload-box {width: 300px;height: 150px;border: 2px dashed #008fe9;border-radius: 10px;display: flex;align-items: center;justify-content: center;color: #333;cursor: pointer;text-align: center;transition: background-color 0.3s;
}.upload-box:hover {background-color: #f0f8ff;
}.file-info {margin-top: 10px;text-align: center;
}.file-info button {background-color: #008fe9;color: #fff;border: none;padding: 8px 16px;border-radius: 4px;cursor: pointer;transition: background-color 0.3s;
}.file-info button:hover {background-color: #005f99;
}
</style>
在前端页面解析,使用原生的input或者el-upload的accept属性只能进行文件类型校验阻止上传,在文件上传选择窗口还是有所有选项(*.*)这个选项,怎么禁用这个浏览器或操作系统自带的文件选择窗口中的所有选项?
思路:
经查阅资料了解,Chrome 86 版本后引入了一项新的 File System API,可以在调用 showOpenFilePicker
方法时指定 excludeAcceptAllOption
参数为 false 来禁用掉“所有文件”。
缺点:兼容性,目前仅 Chrome 86+ 版本支持,其他浏览器都未支持这一特性。
知识抢先看:
File System API
是一种现代的 Web API,允许 Web 应用更直接地访问用户的文件系统。它可以在前端应用中提供类似桌面应用的文件管理功能,支持文件的创建、读写、删除等操作。
- 打开文件选择器:
showOpenFilePicker()
用于打开文件选择器,用户可以选择文件或文件夹。async function openFile() {try {const [fileHandle] = await window.showOpenFilePicker({types: [{description: 'Text files',accept: { 'text/plain': ['.txt'] }}],excludeAcceptAllOption: true,multiple: false});const file = await fileHandle.getFile();console.log('Selected file:', file);} catch (error) {console.error('Error:', error);} }
- 读取文件内容:通过
fileHandle.getFile()
可以获得用户选择的文件,之后可以使用 File API 读取文件内容。async function readFile(fileHandle) {const file = await fileHandle.getFile();const text = await file.text();console.log('File content:', text); }
- 写入文件内容:
createWritable()
用于获取一个可写入的流,允许将数据写入文件。async function writeFile(fileHandle, content) {const writable = await fileHandle.createWritable();await writable.write(content);await writable.close(); }
- 保存文件:通过
showSaveFilePicker()
让用户选择保存位置并创建文件。async function saveFile() {const options = {types: [{description: 'Text files',accept: { 'text/plain': ['.txt'] }}]};const fileHandle = await window.showSaveFilePicker(options);await writeFile(fileHandle, 'Hello, world!'); }
- 访问目录:
showDirectoryPicker()
可以让用户选择一个文件夹,并在该文件夹内创建、读取或删除文件。async function openDirectory() {const directoryHandle = await window.showDirectoryPicker();for await (const entry of directoryHandle.values()) {console.log(entry.kind, entry.name);} }
应用:
使用 showOpenFilePicker
API 实现文件选择,并禁用“所有文件”选项。
<template><el-button @click="openFilePicker">自定义文件选择器</el-button></template><script setup>
async function openFilePicker () {try {const [fileHandle] = await window.showOpenFilePicker({types: [{description: '矢量文件',accept: {'*/*': ['.kml', '.shp'] // 接受所有 MIME 类型,但限制扩展名}}],excludeAcceptAllOption: true, // 禁用“所有文件”选项multiple: false // 限制单个文件});const file = await fileHandle.getFile();console.log('选择文件:', file);// 实现想要的操作逻辑} catch (error) {console.error('文件获取错误:', error);}
}
</script>