对于 Capacitor 应用来说,最好的 cordova-plugin-file-transfer 替代方案取决于一个问题:传输是否需要在应用进入后台后继续运行?对于带进度显示的普通上传和下载,请使用官方 @capacitor/file-transfer 插件。对于大文件、不稳定的网络,或用户切换应用时必须继续进行的传输,请使用 @capgo/capacitor-uploader 以及 @capgo/capacitor-downloader,它们运行在原生后台传输 API 上。本指南将对它们进行对比,并映射每一个 FileTransfer 调用。
cordova-plugin-file-transfer 是否仍处于弃用状态?
这段历史比较混乱,以下是事实:
- 2017 年,Apache Cordova 团队宣布计划弃用该插件,并引导开发者转向
XMLHttpRequestLevel 2。 - 该插件在 npm 上从未被标记为弃用。2.0.0 版本于 2023 年 9 月发布,2.0.1 版本于 2026 年 8 月发布。GitHub 仓库目前仍处于活跃状态。
所以它并没有消亡。但在 Capacitor 应用中,它仍然是一个 Cordova 插件,它会:
- 依赖于
cordova-plugin-file及其cdvfile://以及file://路径模型, - 不符合 Capacitor 的
Filesystem目录和 URI 转换, - 不支持后台传输,
- 使用回调式 API。
如果你正在从 Cordova 迁移,用原生 Capacitor 插件替换它,就能消除继续保留 Cordova 的最后几个理由之一 cordova-plugin-file.
为什么“直接用 fetch”在处理大文件时会失效
Apache 建议的替代方案 XHR 适用于小型 JSON 和图片。但在混合应用进行真正的文件传输时,它会失败:
- 内存。
fetch而 XHR 会给你一个Blob或ArrayBuffer在 WebView 中。一个 500 MB 的视频意味着 WebView 内存中要占用 500 MB,然后还要通过 bridge 进行一次拷贝。这会导致中端 Android 设备上的渲染进程崩溃。 - 挂起。 当用户切换应用时,iOS 会挂起 WebView,导致进行中的请求停滞或失败。
- 缺乏系统集成。 没有系统级下载通知,没有“仅限 Wi-Fi”选项,连接中断后也无法恢复。
Native plugins 直接在磁盘和网络之间流式传输,让 JavaScript 不介入数据路径。
方案对比
| 需要 | cordova-plugin-file-transfer | @capacitor/file-transfer | @capgo/capacitor-uploader | @capgo/capacitor-downloader |
|---|---|---|---|---|
| 下载到磁盘 | 是 | 是 | 否 | 是 |
| 从本地磁盘上传 | 是 | 是 | 是 | 否 |
| 进度事件 | 是 | 可以(progress listener) |
可以(events) |
可以(downloadProgress) |
| Multipart 表单数据 | 是 | 是(默认) | 是 | 不适用 |
| 原始二进制 PUT(预签名 URL) | 是 | 是(设置 Content-Type) |
可以(uploadType: 'binary') |
不适用 |
| 单次请求多个文件 | 否 | 否 | 可以(files) |
不适用 |
| 取消 | abort() |
否 | removeUpload |
stop |
| 暂停 / 恢复 | 否 | 否 | 否 | pause, resume |
| 重试次数 | 否 | 否 | maxRetries |
否 |
| 后台持续运行 | 否 | 应用处于活跃状态时 | 是 | 是 |
| 仅限 Wi-Fi | 否 | 否 | 否 | network: 'wifi-only' |
| HTTP 错误详情 | http_status, body |
httpStatus, body |
事件中的状态 code | 事件中的错误字符串 |
| Web 实现方式 | 否 | 是 | 是(基于 fetch) | 否 |
大多数应用最终会选择 @capacitor/file-transfer 用于小型、用户可见的传输,并使用 Capgo 插件处理媒体上传队列和离线内容下载。
方案 1:@capacitor/file-transfer
bun add @capacitor/file-transfer @capacitor/filesystem
bunx cap sync
该插件需要完整的原生路径。请从以下位置获取: @capacitor/filesystem.
下载
import { FileTransfer } from '@capacitor/file-transfer';
import { Filesystem, Directory } from '@capacitor/filesystem';
export async function downloadReport(url: string) {
const { uri } = await Filesystem.getUri({
directory: Directory.Data,
path: 'reports/latest.pdf',
});
const progress = await FileTransfer.addListener('progress', (p) => {
if (p.type === 'download' && p.lengthComputable) {
console.log(`Downloaded ${Math.round((p.bytes / p.contentLength) * 100)}%`);
}
});
try {
const result = await FileTransfer.downloadFile({
url,
path: uri,
progress: true,
headers: { Authorization: 'Bearer <token>' },
});
return result.path;
} catch (error: any) {
if (error.code === 'OS-PLUG-FLTR-0010') {
console.error('HTTP error', error.data?.httpStatus, error.data?.body);
}
throw error;
} finally {
await progress.remove();
}
}
上传
export async function uploadAvatar(path: string) {
const result = await FileTransfer.uploadFile({
url: 'https://api.example.com/v1/avatar',
path,
fileKey: 'avatar',
mimeType: 'image/jpeg',
headers: { Authorization: 'Bearer <token>' },
progress: true,
});
console.log(result.responseCode, result.response);
}
上传默认使用 multipart 格式。若需原始流(例如上传到预签名 S3 URL),请设置 method: 'PUT' 以及显式的 Content-Type 请求头,例如 application/octet-stream.
错误码遵循 OS-PLUG-FLTR-XXXX 格式。有用的: 0007 文件未找到, 0008 连接失败, 0010 HTTP 错误状态,带有 httpStatus 以及 body 中的 error.data.
方案 2:使用 @capgo/capacitor-uploader 进行后台上传
bun add @capgo/capacitor-uploader
bunx cap sync
import { Uploader } from '@capgo/capacitor-uploader';
await Uploader.addListener('events', async (event) => {
switch (event.name) {
case 'uploading':
console.log(event.id, `${event.payload.percent}%`);
break;
case 'completed':
console.log(event.id, 'done', event.payload.statusCode);
break;
case 'failed':
console.error(event.id, event.payload.error);
break;
}
// Completed and failed events are cached natively until acknowledged
if (event.eventId) {
await Uploader.acknowledgeEvent({ eventId: event.eventId });
}
});
const { id } = await Uploader.startUpload({
filePath: 'file:///path/to/video.mp4',
serverUrl: 'https://api.example.com/v1/videos',
method: 'POST',
uploadType: 'multipart',
fileField: 'video',
parameters: { albumId: '7' },
headers: { Authorization: 'Bearer <token>' },
maxRetries: 3,
notificationTitle: 'Uploading video', // Android only
});
对于预签名 URL,请使用 method: 'PUT',默认使用二进制 body:
await Uploader.startUpload({
filePath,
serverUrl: presignedUrl,
method: 'PUT',
headers: { 'Content-Type': 'video/mp4' },
mimeType: 'video/mp4',
});
在一个 multipart 请求中上传多个文件:
await Uploader.startUpload({
serverUrl: 'https://api.example.com/v1/photos',
method: 'POST',
uploadType: 'multipart',
files: [
{ filePath: photo1, fieldName: 'images[]', mimeType: 'image/jpeg' },
{ filePath: photo2, fieldName: 'images[]', mimeType: 'image/jpeg' },
],
headers: { Authorization: 'Bearer <token>' },
});
使用 Uploader.removeUpload({ id }).
为什么要确认事件? 已完成和失败的事件会存储在持久化原生缓存中,以便在应用关闭或进入后台且尚未处理时重新投递。确认事件会将其移除,防止插件下次初始化时重新广播。
iOS 配置。 上传器使用后台 URLSession。许多应用无需额外配置。如果需要在应用挂起后继续上传,请添加 fetch 改为 UIBackgroundModes。除非其他功能使用 processing ,否则不要添加 BGTaskScheduler,因为 App Store Connect 会拒绝声明了该权限但未实际使用的构建 BGTaskSchedulerPermittedIdentifiers.
方案 3:使用 @capgo/capacitor-downloader 进行后台下载
bun add @capgo/capacitor-downloader
bunx cap sync
import { CapacitorDownloader } from '@capgo/capacitor-downloader';
await CapacitorDownloader.addListener('downloadProgress', ({ id, progress }) => {
console.log(id, `${progress}%`);
});
await CapacitorDownloader.addListener('downloadCompleted', ({ id }) => {
console.log(id, 'completed');
});
await CapacitorDownloader.addListener('downloadFailed', ({ id, error }) => {
console.error(id, error);
});
await CapacitorDownloader.download({
id: 'course-42',
url: 'https://cdn.example.com/courses/42.zip',
destination: 'courses/42.zip',
headers: { Authorization: 'Bearer <token>' },
network: 'wifi-only',
priority: 'normal',
notification: 'progress', // Android: remove the system notification when done
});
// Later
await CapacitorDownloader.pause({ id: 'course-42' });
await CapacitorDownloader.resume({ id: 'course-42' });
const status = await CapacitorDownloader.checkStatus({ id: 'course-42' });
console.log(status.state, status.progress);
相对路径 destination 在 iOS 上解析为应用的 Documents 目录,在 Android 上解析为应用专属的外部文件目录。您也可以传入绝对路径或 file:// URL。使用 getFileInfo({ path }) 在下载后读取文件大小和 MIME 类型,并使用 stop({ id }) 取消并删除部分数据。
从 cordova-plugin-file-transfer 迁移
下载
// Before
const ft = new FileTransfer();
ft.onprogress = (e) => e.lengthComputable && setProgress(e.loaded / e.total);
ft.download(
encodeURI(url),
cordova.file.dataDirectory + 'report.pdf',
(entry) => console.log(entry.toURL()),
(err) => console.error(err.code, err.http_status),
false,
{ headers: { Authorization: 'Bearer <token>' } },
);
// After
const { uri } = await Filesystem.getUri({ directory: Directory.Data, path: 'report.pdf' });
await FileTransfer.downloadFile({
url,
path: uri,
progress: true,
headers: { Authorization: 'Bearer <token>' },
});
上传
// Before
const options = new FileUploadOptions();
options.fileKey = 'file';
options.fileName = 'photo.jpg';
options.mimeType = 'image/jpeg';
options.params = { albumId: '7' };
options.headers = { Authorization: 'Bearer <token>' };
new FileTransfer().upload(fileUrl, encodeURI(serverUrl), onSuccess, onError, options);
// After, foreground
await FileTransfer.uploadFile({
url: serverUrl,
path: fileUrl,
fileKey: 'file',
mimeType: 'image/jpeg',
params: { albumId: '7' },
headers: { Authorization: 'Bearer <token>' },
});
// After, background
await Uploader.startUpload({
filePath: fileUrl,
serverUrl,
uploadType: 'multipart',
fileField: 'file',
parameters: { albumId: '7' },
headers: { Authorization: 'Bearer <token>' },
});
请注意 params 中的 @capacitor/file-transfer 是 URL 查询参数。如果您的服务器期望接收表单字段,请使用 Capgo 上传器的 parameters,它们以 multipart 表单字段的形式发送。
映射表
| cordova-plugin-file-transfer | @capacitor/file-transfer | Capgo 插件 |
|---|---|---|
download(source, target, ...) |
downloadFile({ url, path }) |
CapacitorDownloader.download({ id, url, destination }) |
upload(file, server, ...) |
uploadFile({ url, path }) |
Uploader.startUpload({ filePath, serverUrl }) |
onprogress |
addListener('progress') |
addListener('events') / addListener('downloadProgress') |
abort() |
不可用 | removeUpload({ id }) / stop({ id }) |
options.fileKey |
fileKey |
fileField 或 files[].fieldName |
options.params |
params (查询字符串) |
parameters (表单字段) |
options.httpMethod |
method |
method (POST 或 PUT) |
options.chunkedMode |
chunkedMode |
原生处理 |
cordova.file.dataDirectory |
Filesystem.getUri({ directory: Directory.Data }) |
相对路径或 file:// URL |
FileTransferError.http_status |
error.data.httpStatus |
event.payload.statusCode |
路径:迁移中最容易出问题的部分
Cordova code 到处传递路径 cdvfile:// 或 cordova.file.* 在 Capacitor 中:
- 从以下来源获取原生路径:
Filesystem.getUri()或从创建文件的插件(相机、文件选择器、录音机)获取。 - 要在 WebView 中显示下载的图片,请用以下方式转换原生路径:
Capacitor.convertFileSrc(path). - Capgo 有一个 文件插件 以及 文件选择器 如果
@capacitor/filesystem无法覆盖您的场景。
故障排除
上传在 Android 上正常,但在 iOS 上因文件未找到而失败。 您传入的是 capacitor:// WebView URL,而不是原生 file:// 路径。请使用 Filesystem.getUri().
服务器以 415 拒绝上传。 Multipart 与原始请求体不匹配。对于预签名 URL,请使用 PUT 并显式指定 Content-Type.
iOS 上后台上传会停止。 用户强制退出应用,导致后台会话被取消,或者 fetch 需要时缺少后台模式。
Android 显示卡住的下载通知。 设置 notification: 'progress' 或 'hidden' 在下载器上。
大文件下载时内存崩溃。 你仍在使用 fetch 或通过 bridge 以 base64 读取文件。请切换到原生磁盘下载。
发布迁移
更换原生插件需要新的商店构建,这会 Capgo 构建 可在云端为 iOS 和 Android 运行。之后,驱动上传和下载的 JavaScript 可以通过 Capgo 实时更新进行修复和改进。关于从 Cordova 迁移到 Capacitor 的其余部分,请参阅我们的 迁移指南,以及其他付费或旧版原生 SDK,请参阅 Ionic Enterprise 插件替代方案.