跳到主要内容

跨页面文件传输方案

记录一下我在开发中遇到的跨前端页面进行文件传输的方案。 我的场景是,我在 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 });
}