跨页面文件传输方案
记录一下我在开发中遇到的跨前端页面进行文件传输的方案。 我的场景是,我在 A 页面接收到了用户上传的 pptx 文件,并且跳转到 B 页面后,在 B 页面获取用户在 A 页面上传的文件;A 页面与 b 页面是不同的域名的网页,并且不确定用户上传的文件的大小。以下是可选的方案:
1. 服务器端存储
将文件上传到页面 A 的服务器,然后在页面 B 上检索:
页面 A:
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const { fileId } = await response.json();
// Pass fileId to page B via URL parameter
window.location.href = `https://domainB.com/pageB?fileId=${fileId}`;
}
页面 B:
async function retrieveFile() {
const urlParams = new URLSearchParams(window.location.search);
const fileId = urlParams.get('fileId');
const response = await fetch(`/api/files/${fileId}`);
const blob = await response.blob();
return blob;
}
2. 临时云存储
使用 AWS S3、Google Cloud Storage 等服务:
// Page A - Upload to cloud
async function uploadToCloud(file) {
// Generate presigned URL or use cloud SDK
const uploadUrl = await getPresignedUploadUrl();
await fetch(uploadUrl, {
method: 'PUT',
body: file
});
const fileKey = generateFileKey();
window.location.href = `https://domainB.com/pageB?key=${fileKey}`;
}
// Page B - Download from cloud
async function downloadFromCloud(key) {
const downloadUrl = await getPresignedDownloadUrl(key);
const response = await fetch(downloadUrl);
return await response.blob();
}
3. 使用 IndexedDB 进行文件处理(客户端)
对大文件进行分块并本地存储:
页面 A:
async function storeFileInChunks(file) {
const chunkSize = 1024 * 1024; // 1MB chunks
const chunks = Math.ceil(file.size / chunkSize);
const fileId = generateUniqueId();
const db = await openIndexedDB();
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
await storeChunk(db, fileId, i, chunk);
}
// Store metadata
await storeMetadata(db, fileId, {
name: file.name,
type: file.type,
size: file.size,
chunks: chunks
});
// Navigate with fileId
localStorage.setItem('pendingFileId', fileId);
window.location.href = 'https://domainB.com/pageB';
}
function openIndexedDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('FileStorage', 1);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains('chunks')) {
db.createObjectStore('chunks');
}
if (!db.objectStoreNames.contains('metadata')) {
db.createObjectStore('metadata');
}
};
request.onsuccess = (e) => resolve(e.target.result);
request.onerror = (e) => reject(e.target.error);
});
}
页面 B:
async function retrieveFileFromChunks() {
const fileId = localStorage.getItem('pendingFileId');
if (!fileId) return null;
const db = await openIndexedDB();
const metadata = await getMetadata(db, fileId);
if (!metadata) return null;
const chunks = [];
for (let i = 0; i < metadata.chunks; i++) {
const chunk = await getChunk(db, fileId, i);
chunks.push(chunk);
}
const blob = new Blob(chunks, { type: metadata.type });
// Clean up
await cleanupFile(db, fileId);
localStorage.removeItem('pendingFileId');
return new File([blob], metadata.name, {
type: metadata.type,
lastModified: Date.now()
});
}
4. 带有 URL 参数的 Base64 编码(仅限小文件)
仅适用于小文件,小于2KB,由于 URL 长度限制:
// Page A
function encodeFileToURL(file) {
const reader = new FileReader();
reader.onload = (e) => {
const base64 = btoa(e.target.result);
const params = new URLSearchParams({
fileName: file.name,
fileType: file.type,
fileData: base64
});
window.location.href = `https://domainB.com/pageB?${params}`;
};
reader.readAsBinaryString(file);
}
// Page B
function decodeFileFromURL() {
const params = new URLSearchParams(window.location.search);
const fileName = params.get('fileName');
const fileType = params.get('fileType');
const fileData = params.get('fileData');
if (!fileData) return null;
const binaryString = atob(fileData);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return new File([bytes], fileName, { type: fileType });
}