在航空航天领域的数据处理中,卫星遥感数据的上传是一个常见但极具挑战性的需求。这类数据通常具有以下特点:
我们最初采用百度WebUploader组件,但在实际应用中发现了几个关键问题:
我们对主流大文件上传方案进行了横向对比:
| 方案 | 优点 | 缺点 | 适用性评估 |
|---|---|---|---|
| WebUploader | 中文文档完善,社区资源多 | 维护停滞,兼容性问题 | 不满足长期需求 |
| Uppy | 现代架构,插件丰富 | 学习曲线陡,体积大 | 适合复杂场景但过度设计 |
| Resumable.js | 专注分片上传 | UI简陋,功能单一 | 基础功能可用但扩展性差 |
| 原生实现 | 完全可控,轻量 | 开发成本高 | 最适合专业领域需求 |
基于卫星遥感数据的特殊性,我们确定了以下设计原则:
javascript复制// 改进后的分片函数(支持卫星数据专用校验)
const chunkFile = async (file, chunkSize = 5 * 1024 * 1024) => {
const chunks = []
let current = 0
const fileHash = await calculateFileHash(file) // 全文件SHA-256计算
while (current < file.size) {
const chunk = file.slice(current, current + chunkSize)
const chunkHash = await calculateChunkHash(chunk)
chunks.push({
file: chunk,
chunkIndex: chunks.length,
totalChunks: Math.ceil(file.size / chunkSize),
fileHash,
chunkHash,
// 其他元数据...
})
current += chunkSize
}
return chunks
}
// 使用Web Crypto API计算哈希
const calculateHash = async (data, algorithm = 'SHA-256') => {
const buffer = data instanceof Blob ? await data.arrayBuffer() : data
const hash = await crypto.subtle.digest(algorithm, buffer)
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
}
关键改进:
typescript复制// FileUploader.vue
import { ref, computed } from 'vue'
import type { UploadChunk, UploadStatus } from './types'
export default {
setup() {
const state = reactive({
file: null as File | null,
chunks: [] as UploadChunk[],
status: 'idle' as UploadStatus,
progress: 0,
error: null as Error | null,
currentChunk: 0,
controller: null as AbortController | null
})
const uploadRate = computed(() => {
// 计算实时上传速率(MB/s)
})
const estimatedTime = computed(() => {
// 计算剩余时间
})
const chunkFile = async (file: File) => {
// 分片实现...
}
const startUpload = async () => {
if (!state.file) return
state.controller = new AbortController()
state.status = 'uploading'
try {
for (let i = state.currentChunk; i < state.chunks.length; i++) {
if (state.status !== 'uploading') break
const chunk = state.chunks[i]
const formData = new FormData()
// 构建包含校验数据的分片表单
appendChunkToFormData(formData, chunk)
await uploadChunk(formData, state.controller.signal)
state.currentChunk = i + 1
state.progress = Math.round(((i + 1) / state.chunks.length) * 100)
// 保存上传状态到IndexedDB
await saveUploadState()
}
if (state.status === 'uploading') {
await mergeChunks()
state.status = 'completed'
}
} catch (err) {
handleUploadError(err)
}
}
return {
state,
uploadRate,
estimatedTime,
startUpload,
pauseUpload: () => {
state.controller?.abort()
state.status = 'paused'
}
}
}
}
PHP服务端增加了严格的校验逻辑:
php复制// upload-chunk.php
$chunkHash = $_POST['chunkHash'] ?? '';
$receivedHash = hash_file('sha256', $_FILES['file']['tmp_name']);
if ($chunkHash !== $receivedHash) {
http_response_code(422);
echo json_encode([
'status' => 'error',
'code' => 'CHUNK_HASH_MISMATCH',
'message' => '分片校验失败',
'expected' => $chunkHash,
'actual' => $receivedHash
]);
exit;
}
// 只有校验通过才保存分片
if (move_uploaded_file($_FILES['file']['tmp_name'], $chunkPath)) {
// 记录校验成功的分片信息
file_put_contents(
$statusFile,
json_encode([
'verifiedChunks' => array_merge(
$status['verifiedChunks'] ?? [],
[['index' => $chunkIndex, 'hash' => $chunkHash]]
)
])
);
}
针对遥感数据特点,我们实现了:
javascript复制// 卫星数据头文件校验
const validateSatelliteHeader = (chunk) => {
const HEADER_MAGIC = [0x53, 0x41, 0x54, 0x48]; // "SATH"
const header = new Uint8Array(chunk.slice(0, 4));
return HEADER_MAGIC.every((val, i) => val === header[i]);
}
根据网络状况自动调整分片大小:
javascript复制const getDynamicChunkSize = () => {
const connection = navigator.connection
if (!connection) return DEFAULT_CHUNK_SIZE
switch (connection.effectiveType) {
case '4g': return 10 * 1024 * 1024 // 10MB
case '3g': return 2 * 1024 * 1024 // 2MB
default: return DEFAULT_CHUNK_SIZE // 5MB
}
}
实现智能并发队列:
javascript复制class UploadQueue {
constructor(maxConcurrent = 3) {
this.queue = []
this.active = 0
this.max = maxConcurrent
}
add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject })
this.next()
})
}
next() {
while (this.active < this.max && this.queue.length) {
const { task, resolve, reject } = this.queue.shift()
this.active++
task()
.then(resolve)
.catch(reject)
.finally(() => {
this.active--
this.next()
})
}
}
}
| 错误类型 | 处理策略 | 重试机制 |
|---|---|---|
| 网络中断 | 指数退避重试 | 最多5次 |
| 校验失败 | 立即重传分片 | 最多3次 |
| 服务端错误 | 暂停并报警 | 人工介入 |
| 客户端错误 | 停止上传 | 需刷新页面 |
javascript复制// 上报关键指标
const reportMetrics = (metrics) => {
const analyticsEndpoint = '/api/upload-metrics'
navigator.sendBeacon(analyticsEndpoint, JSON.stringify({
...metrics,
userAgent: navigator.userAgent,
timestamp: Date.now(),
fileType: state.file?.type,
fileSize: state.file?.size
}))
}
// 监控关键事件
onMounted(() => {
watch(() => state.status, (newVal) => {
reportMetrics({
event: `upload_${newVal}`,
duration: uploadTimer.value,
chunksUploaded: state.currentChunk
})
})
})
我们发现并解决了以下浏览器特定问题:
PHP配置关键参数:
ini复制; php.ini 调整
upload_max_filesize = 10G
post_max_size = 12G
max_execution_time = 3600
memory_limit = 2G
在真实卫星数据上传场景中的表现:
| 指标 | 改进前 | 改进后 | 提升幅度 |
|---|---|---|---|
| 成功率 | 82% | 99.7% | +17.7% |
| 平均耗时 | 42min | 28min | -33% |
| 重传率 | 18% | 2.3% | -87% |
| CPU占用 | 高 | 中等 | -40% |
在实际部署中,我们总结出几点关键经验:
对于需要处理超大遥感数据的团队,建议先在小规模测试中验证核心流程,再逐步扩展功能。我们开源的实现方案已经过多个卫星项目验证,可以作为可靠的起点进行二次开发。