Upload 文件上传
文件上传组件,支持单文件、多文件上传,文件类型限制等功能。
基础用法
单文件上传。
html
<div id="demo-upload"></div>
<div id="output-basic" style="margin-top: 12px; font-size: 13px; color: #6b7280;">未选择文件</div>
<div style="margin-top: 12px; display: flex; gap: 8px;">
<button class="sa-button" id="btn-open">打开文件选择</button>
<button class="sa-button" id="btn-reset">重置</button>
</div>
<script>
const upload = new SaUpload('#demo-upload', {
onSelect: (files) => {
const output = document.getElementById('output-basic');
if (files && files.length > 0) {
output.textContent = `已选择 ${files.length} 个文件: ${Array.from(files).map(f => f.name).join(', ')}`;
} else {
output.textContent = '未选择文件';
}
console.log('选择的文件:', files);
}
});
document.getElementById('btn-open').addEventListener('click', () => {
upload.open();
});
document.getElementById('btn-reset').addEventListener('click', () => {
upload.reset();
document.getElementById('output-basic').textContent = '未选择文件';
});
SA.init('body');
</script>加载 SanoUI 组件中...
多文件上传
通过 multiple: true 启用多文件选择功能。
html
<div id="demo-upload-multiple"></div>
<div id="output-multiple" style="margin-top: 12px; font-size: 13px; color: #6b7280;">未选择文件</div>
<script>
const upload = new SaUpload('#demo-upload-multiple', {
multiple: true,
onSelect: (files) => {
const output = document.getElementById('output-multiple');
if (files && files.length > 0) {
output.textContent = `已选择 ${files.length} 个文件: ${Array.from(files).map(f => f.name).join(', ')}`;
} else {
output.textContent = '未选择文件';
}
console.log('选择的文件:', files);
}
});
SA.init('body');
</script>加载 SanoUI 组件中...
文件类型限制
通过 accept 参数限制可选择的文件类型,通过 hint 显示提示信息。
html
<div id="demo-upload-accept"></div>
<div id="output-accept" style="margin-top: 12px; font-size: 13px; color: #6b7280;">未选择文件</div>
<script>
const upload = new SaUpload('#demo-upload-accept', {
accept: '.jpg,.jpeg,.png,.gif',
hint: '只能上传 JPG、PNG、GIF 格式的图片',
onSelect: (files) => {
const output = document.getElementById('output-accept');
if (files && files.length > 0) {
output.textContent = `已选择 ${files.length} 个文件: ${Array.from(files).map(f => f.name).join(', ')}`;
} else {
output.textContent = '未选择文件';
}
console.log('选择的文件:', files);
}
});
SA.init('body');
</script>加载 SanoUI 组件中...
自动初始化
使用 SA.init() 可以自动初始化所有带有 data-upload 属性的文件上传组件。
html
<div id="demo-auto-init"
data-upload="true"
data-text="自动初始化上传"
data-hint="支持所有文件类型"
data-multiple="false"></div>
<div style="margin-top: 12px; font-size: 14px; color: #606266;">
提示:文件上传组件已通过 SA.init() 自动初始化。
</div>
<script>
// 使用 SA.init() 自动初始化所有组件
const page = SA.init('body');
// 文件上传组件已自动初始化,所有带有 data-upload 属性的元素都会被处理
// 文件上传组件通常不需要手动初始化,直接使用 HTML 属性即可
</script>加载 SanoUI 组件中...
API
构造函数
javascript
new SaUpload(container, config)参数
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
container | 容器选择器或元素 | string | HTMLElement | - |
config | 配置选项 | SaUploadOptions | {} |
配置选项
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
multiple | 是否支持多文件选择 | boolean | false |
text | 按钮显示文本 | string | '上传文件' |
hint | 提示信息 | string | '' |
accept | 接受的文件类型(MIME 类型或扩展名) | string | '' |
onSelect | 文件选择回调 | function(files, helpers): void | null |
Data 属性
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
data-upload | 启用自动初始化 | boolean | - |
data-multiple | 是否支持多文件 | boolean | false |
data-text | 按钮显示文本 | string | '上传文件' |
data-hint | 提示信息 | string | '' |
data-accept | 接受的文件类型 | string | '' |
方法
| 方法名 | 说明 | 参数 | 返回值 |
|---|---|---|---|
open() | 打开文件选择对话框 | - | void |
reset() | 重置文件选择(清空已选择的文件) | - | void |
destroy() | 销毁实例 | - | void |
事件回调
| 事件名 | 说明 | 回调参数 |
|---|---|---|
onSelect | 文件选择时触发 | files: File[] - 选择的文件数组helpers: { reset: Function } - 辅助方法对象 |
静态方法
| 方法名 | 说明 | 参数 | 返回值 |
|---|---|---|---|
initAll(container?) | 初始化容器内所有带 data-upload 的元素 | container?: Element | string - 容器元素或选择器 | Array<SaUpload> |
init(selector) | 初始化指定容器内的所有标记元素 | selector: Element | string - 容器选择器或元素 | Array<SaUpload> |
实际使用场景
场景 1:图片上传预览
上传图片后显示预览图。
html
<div id="image-upload"></div>
<div id="image-preview" style="margin-top: 1rem; display: none;">
<img id="preview-img" style="max-width: 300px; max-height: 300px; border: 1px solid #ddd; border-radius: 4px;">
</div>
<script>
SA.init('body');
const upload = new SaUpload('#image-upload', {
accept: '.jpg,.jpeg,.png,.gif',
hint: '支持 JPG、PNG、GIF 格式,最大 5MB',
text: '选择图片',
onSelect: (files) => {
if (files.length > 0) {
const file = files[0];
// 检查文件大小(5MB)
if (file.size > 5 * 1024 * 1024) {
alert('文件大小不能超过 5MB');
return;
}
// 创建预览
const reader = new FileReader();
reader.onload = (e) => {
const previewImg = document.getElementById('preview-img');
const previewDiv = document.getElementById('image-preview');
previewImg.src = e.target.result;
previewDiv.style.display = 'block';
};
reader.readAsDataURL(file);
}
}
});
</script>加载 SanoUI 组件中...
场景 2:多文件上传列表
显示已选择文件的列表,支持删除。
html
<div id="multi-upload"></div>
<div id="file-list" style="margin-top: 1rem;"></div>
<script>
SA.init('body');
let selectedFiles = [];
const upload = new SaUpload('#multi-upload', {
multiple: true,
text: '选择文件',
hint: '支持多文件选择',
onSelect: (files) => {
selectedFiles = Array.from(files);
renderFileList();
}
});
function renderFileList() {
const fileList = document.getElementById('file-list');
if (selectedFiles.length === 0) {
fileList.innerHTML = '<p style="color: #999;">未选择文件</p>';
return;
}
fileList.innerHTML = selectedFiles.map((file, index) => {
const size = (file.size / 1024).toFixed(2) + ' KB';
return `
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 0.5rem;">
<div>
<strong>${file.name}</strong>
<span style="color: #999; margin-left: 0.5rem;">${size}</span>
</div>
<button class="sa-button sa-button--text sa-button--danger" onclick="removeFile(${index})">删除</button>
</div>
`;
}).join('');
}
function removeFile(index) {
selectedFiles.splice(index, 1);
renderFileList();
}
// 初始渲染
renderFileList();
</script>加载 SanoUI 组件中...
场景 3:文件上传到服务器
选择文件后上传到服务器。
html
<div id="server-upload"></div>
<div id="upload-status" style="margin-top: 1rem; padding: 1rem; background: #f5f5f5; border-radius: 4px; display: none;">
<div id="upload-progress" style="margin-bottom: 0.5rem;">上传进度: 0%</div>
<div id="upload-result"></div>
</div>
<script>
SA.init('body');
const upload = new SaUpload('#server-upload', {
text: '选择文件上传',
hint: '支持所有文件类型',
onSelect: async (files) => {
if (files.length === 0) return;
const statusDiv = document.getElementById('upload-status');
const progressDiv = document.getElementById('upload-progress');
const resultDiv = document.getElementById('upload-result');
statusDiv.style.display = 'block';
progressDiv.textContent = '上传中...';
resultDiv.textContent = '';
const file = files[0];
const formData = new FormData();
formData.append('file', file);
try {
// 模拟上传(实际使用时替换为真实 API)
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
progressDiv.textContent = `上传进度: ${percent}%`;
}
};
xhr.onload = () => {
if (xhr.status === 200) {
progressDiv.textContent = '上传完成';
resultDiv.innerHTML = `<span style="color: #67c23a;">✓ 文件上传成功: ${file.name}</span>`;
} else {
progressDiv.textContent = '上传失败';
resultDiv.innerHTML = `<span style="color: #f56c6c;">✗ 上传失败,请重试</span>`;
}
};
xhr.onerror = () => {
progressDiv.textContent = '上传失败';
resultDiv.innerHTML = `<span style="color: #f56c6c;">✗ 网络错误,请检查连接</span>`;
};
// 模拟上传(实际使用时取消注释)
// xhr.open('POST', '/api/upload');
// xhr.send(formData);
// 模拟上传成功
setTimeout(() => {
progressDiv.textContent = '上传完成';
resultDiv.innerHTML = `<span style="color: #67c23a;">✓ 文件上传成功: ${file.name}</span>`;
}, 2000);
} catch (error) {
progressDiv.textContent = '上传失败';
resultDiv.innerHTML = `<span style="color: #f56c6c;">✗ ${error.message}</span>`;
}
}
});
</script>加载 SanoUI 组件中...
场景 4:文件类型和大小验证
在上传前验证文件类型和大小。
html
<div id="validated-upload"></div>
<div id="validation-result" style="margin-top: 1rem; padding: 1rem; background: #f5f5f5; border-radius: 4px; display: none;"></div>
<script>
SA.init('body');
const upload = new SaUpload('#validated-upload', {
accept: '.pdf,.doc,.docx',
hint: '仅支持 PDF、Word 文档,最大 10MB',
text: '选择文档',
onSelect: (files) => {
const resultDiv = document.getElementById('validation-result');
if (files.length === 0) {
resultDiv.style.display = 'none';
return;
}
const file = files[0];
const errors = [];
// 验证文件类型
const allowedTypes = ['.pdf', '.doc', '.docx'];
const fileExt = '.' + file.name.split('.').pop().toLowerCase();
if (!allowedTypes.includes(fileExt)) {
errors.push(`不支持的文件类型: ${fileExt}`);
}
// 验证文件大小(10MB)
const maxSize = 10 * 1024 * 1024;
if (file.size > maxSize) {
errors.push(`文件大小超过限制: ${(file.size / 1024 / 1024).toFixed(2)}MB > 10MB`);
}
if (errors.length > 0) {
resultDiv.style.display = 'block';
resultDiv.innerHTML = `
<div style="color: #f56c6c;">
<strong>验证失败:</strong>
<ul style="margin: 0.5rem 0; padding-left: 1.5rem;">
${errors.map(e => `<li>${e}</li>`).join('')}
</ul>
</div>
`;
upload.reset();
} else {
resultDiv.style.display = 'block';
resultDiv.innerHTML = `
<div style="color: #67c23a;">
<strong>✓ 验证通过</strong><br>
文件名: ${file.name}<br>
大小: ${(file.size / 1024).toFixed(2)} KB
</div>
`;
}
}
});
</script>加载 SanoUI 组件中...
场景 5:拖拽上传
支持拖拽文件到指定区域上传。
html
<div id="drag-upload-area" style="border: 2px dashed #ddd; border-radius: 4px; padding: 2rem; text-align: center; cursor: pointer; background: #fafafa;">
<div style="margin-bottom: 0.5rem;">📁 拖拽文件到此处或点击选择</div>
<div style="font-size: 0.875rem; color: #999;">支持多文件,最大 50MB</div>
</div>
<div id="drag-upload" style="display: none;"></div>
<div id="drag-file-list" style="margin-top: 1rem;"></div>
<script>
SA.init('body');
const upload = new SaUpload('#drag-upload', {
multiple: true,
onSelect: (files) => {
renderDragFileList(Array.from(files));
}
});
const dragArea = document.getElementById('drag-upload-area');
const fileList = document.getElementById('drag-file-list');
// 点击区域打开文件选择
dragArea.addEventListener('click', () => {
upload.open();
});
// 拖拽事件
dragArea.addEventListener('dragover', (e) => {
e.preventDefault();
dragArea.style.borderColor = '#409eff';
dragArea.style.background = '#ecf5ff';
});
dragArea.addEventListener('dragleave', () => {
dragArea.style.borderColor = '#ddd';
dragArea.style.background = '#fafafa';
});
dragArea.addEventListener('drop', (e) => {
e.preventDefault();
dragArea.style.borderColor = '#ddd';
dragArea.style.background = '#fafafa';
const files = Array.from(e.dataTransfer.files);
if (files.length > 0) {
renderDragFileList(files);
}
});
function renderDragFileList(files) {
if (files.length === 0) {
fileList.innerHTML = '';
return;
}
fileList.innerHTML = files.map((file, index) => {
const size = (file.size / 1024 / 1024).toFixed(2) + ' MB';
return `
<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 0.5rem; background: white;">
<div>
<strong>${file.name}</strong>
<span style="color: #999; margin-left: 0.5rem;">${size}</span>
</div>
<span style="color: #67c23a;">✓ 已选择</span>
</div>
`;
}).join('');
}
</script>加载 SanoUI 组件中...
注意事项
- 文件类型限制:
accept属性支持 MIME 类型(如image/*)或文件扩展名(如.jpg,.png) - 文件大小:浏览器不提供文件大小限制,需要在
onSelect回调中手动验证 - 多文件选择:设置
multiple: true后,用户可以选择多个文件 - 自动初始化:使用
SA.init()会自动初始化所有带data-upload属性的元素 - 文件对象:
onSelect回调接收的是File对象数组,可以使用FileReaderAPI 读取文件内容
常见问题
Q: 如何获取选择的文件?
A: 在 onSelect 回调中获取:
javascript
const upload = new SaUpload('#upload', {
onSelect: (files) => {
console.log('选择的文件:', files);
files.forEach(file => {
console.log('文件名:', file.name);
console.log('文件大小:', file.size);
console.log('文件类型:', file.type);
});
}
});Q: 如何限制文件大小?
A: 在 onSelect 回调中验证:
javascript
onSelect: (files) => {
const maxSize = 5 * 1024 * 1024; // 5MB
const invalidFiles = files.filter(file => file.size > maxSize);
if (invalidFiles.length > 0) {
alert('文件大小不能超过 5MB');
upload.reset();
return;
}
}Q: 如何实现图片预览?
A: 使用 FileReader API:
javascript
onSelect: (files) => {
if (files.length > 0) {
const reader = new FileReader();
reader.onload = (e) => {
const img = document.createElement('img');
img.src = e.target.result;
document.body.appendChild(img);
};
reader.readAsDataURL(files[0]);
}
}Q: 如何上传文件到服务器?
A: 使用 FormData 和 XMLHttpRequest 或 fetch:
javascript
onSelect: async (files) => {
const formData = new FormData();
formData.append('file', files[0]);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
console.log('上传结果:', result);
}Q: 如何清空已选择的文件?
A: 使用 reset() 方法:
javascript
upload.reset();Q: 如何支持拖拽上传?
A: 监听容器的拖拽事件:
javascript
const container = document.getElementById('upload-container');
container.addEventListener('drop', (e) => {
e.preventDefault();
const files = Array.from(e.dataTransfer.files);
// 处理文件
});