Skip to content

Cordova - 文件传输

Cordova - 文件操作(下载和上传)

Section titled “Cordova - 文件操作(下载和上传)”

cordova-plugin-file-transfer 插件,以前用于此目的,现已 弃用。现代 Cordova 应用应结合使用 cordova-plugin-file 插件来访问和管理设备文件系统上的文件,并使用标准的 Web API(如 Fetch API 或 XMLHttpRequest)进行网络操作(下载和上传文件)。

本教程演示了如何使用这些现代方法从服务器下载文件以及从设备上传文件。

你需要 cordova-plugin-file 插件来与设备的 文件系统 进行交互。Fetch API 是一个内置的浏览器功能,在 Cordova 的 WebView 中也可用。

cordova plugin add cordova-plugin-file

请记住在添加插件后重建项目:cordova build <platform>。

在 www/index.html 中,添加下载和上传按钮,以及一个用于选择上传文件的方式(虽然为简单起见,我们将首先演示上传一个已存在或新创建的文件)。添加一个区域用于状态消息。

<body>
<h1>文件操作演示</h1>
<h2>下载文件</h2>
<button id="downloadFileBtn">下载图片</button>
<h2>上传文件</h2>
<!-- 对于实际应用,你可能会使用 <input type="file"> 或 cordova-plugin-camera 来获取文件 URI -->
<button id="createAndUploadFileBtn">创建并上传文本文件</button>
<div id="statusMessages" style="margin-top: 20px; border: 1px solid #ccc; padding: 10px;">
状态消息将显示在这里...
</div>
<script src="cordova.js"></script>
<script src="js/index.js"></script>
</body>

在 www/js/index.js 中,在 onDeviceReady 函数内,设置事件监听器和核心逻辑。所有文件操作都依赖于 cordova-plugin-file 已准备就绪,这由 deviceready 确保。

// 在 www/js/index.js 中
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
console.log('设备已就绪。文件和 Fetch API 可用。');
document.getElementById('downloadFileBtn').addEventListener('click', downloadFileExample);
document.getElementById('createAndUploadFileBtn').addEventListener('click', createAndUploadFileExample);
}
function updateStatus(message, isError = false) {
const statusDiv = document.getElementById('statusMessages');
const p = document.createElement('p');
p.textContent = message;
if (isError) {
p.style.color = 'red';
console.error(message);
} else {
p.style.color = 'green';
console.log(message);
}
statusDiv.appendChild(p);
}

此函数从一个占位符 URL 下载图片并将其保存到应用的数据目录中。

async function downloadFileExample() {
const fileURL = "https://via.placeholder.com/300/09f/fff.png?text=CordovaDownload"; // 示例图片 URL
const fileName = "downloadedImage.png";
// 目标目录:cordova.file.dataDirectory 是持久的,并且是应用私有的。
// 其他选项:cordova.file.cacheDirectory(用于临时缓存文件)
const targetDirectory = cordova.file.dataDirectory;
updateStatus(`开始下载 ${fileName} 从 ${fileURL}...`);
try {
const response = await fetch(fileURL);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const blob = await response.blob();
updateStatus('文件已作为 blob 获取。');
// 获取目标目录的 DirectoryEntry
const dirEntry = await new Promise((resolve, reject) => {
window.resolveLocalFileSystemURL(targetDirectory, resolve, reject);
});
updateStatus(`目标目录已解析:${dirEntry.toURL()}`);
// 获取新文件的 FileEntry
const fileEntry = await new Promise((resolve, reject) => {
dirEntry.getFile(fileName, { create: true, exclusive: false }, resolve, reject);
});
updateStatus(`文件 entry 已创建/访问:${fileEntry.toURL()}`);
// 将 blob 写入文件
await new Promise((resolve, reject) => {
fileEntry.createWriter((fileWriter) => {
fileWriter.onwriteend = () => {
updateStatus(`文件 ${fileName} 已成功下载到 ${fileEntry.toURL()}`);
resolve();
};
fileWriter.onerror = (e) => {
updateStatus(`写入文件失败:${e.toString()}`, true);
reject(e);
};
fileWriter.write(blob);
}, reject);
});
} catch (error) {
updateStatus(`下载错误:${error.message || error}`, true);
}
}

此函数首先在应用的数据目录中创建一个简单的文本文件,然后将其上传到测试服务器 URL。在实际应用中,你可能会从文件选择器(<input type=“file”>)或另一个插件(如 cordova-plugin-camera)获取文件 URI。

async function createAndUploadFileExample() {
const textContent = `来自 Cordova 的问候!时间戳:${new Date().toISOString()}`;
const fileName = "uploadMe.txt";
const fileDirectory = cordova.file.dataDirectory;
const uploadUrl = "https://jsonplaceholder.typicode.com/posts"; // 示例上传端点(将返回你发送的内容)
updateStatus(`尝试创建并上传 ${fileName}...`);
try {
// 1. 创建文件并写入内容
const dirEntry = await new Promise((resolve, reject) => {
window.resolveLocalFileSystemURL(fileDirectory, resolve, reject);
});
const fileEntry = await new Promise((resolve, reject) => {
dirEntry.getFile(fileName, { create: true, exclusive: false }, resolve, reject);
});
updateStatus(`用于上传的文件 entry:${fileEntry.toURL()}`);
await new Promise((resolve, reject) => {
fileEntry.createWriter((fileWriter) => {
fileWriter.onwriteend = () => {
updateStatus(`内容已写入 ${fileName}。`);
resolve();
};
fileWriter.onerror = (e) => {
reject(new Error(`写入文件失败:${e.toString()}`));
};
const blob = new Blob([textContent], { type: 'text/plain' });
fileWriter.write(blob);
}, (err) => reject(new Error(`FileWriter 创建失败:${err}`)));
});
// 2. 将文件作为 Blob 读取以上传
const fileToUpload = await new Promise((resolve, reject) => {
fileEntry.file(resolve, reject);
}); // fileToUpload 是一个 File 对象(也是一个 Blob)
// 3. 使用 Fetch API 结合 FormData 上传文件
const formData = new FormData();
formData.append('file', fileToUpload, fileName); // 'file' 是服务器期望的字段名称
formData.append('description', 'Cordova 文件上传测试');
updateStatus(`正在上传 ${fileName} 到 ${uploadUrl}...`);
const response = await fetch(uploadUrl, {
method: 'POST',
body: formData,
// 你的服务器可能需要 Headers,例如用于身份验证
// headers: {
// 'Authorization': 'Bearer YOUR_TOKEN_HERE'
// }
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`上传 HTTP 错误!状态:${response.status},消息:${errorText}`);
}
const responseData = await response.json(); // 假设服务器响应 JSON
updateStatus(`上传成功!服务器响应:${JSON.stringify(responseData)}`);
console.log('服务器响应:', responseData);
} catch (error) {
updateStatus(`上传错误:${error.message || error}`, true);
}
}
  • cordova-plugin-file:对于解析文件系统 URL(window.resolveLocalFileSystemURL)、获取 DirectoryEntry 和 FileEntry 对象、读取和写入文件(createWriter、fileEntry.file() 获取 File 对象)至关重要。常见目录包括:
  • cordova.file.dataDirectory:持久的、应用私有的存储空间。
  • cordova.file.cacheDirectory:用于操作系统可以删除的缓存文件。
  • cordova.file.externalDataDirectory:外部存储(例如 SD 卡)的应用私有目录。
  • 有关完整列表及其含义,请参阅插件文档。
  • Fetch API:用于实际的网络请求。fetch(url) 用于 GET (下载),fetch(url, { method: ‘POST’, body: formData }) 用于 POST (上传)。
  • Blob / File 对象:下载通常以 Blob 的形式接收。对于上传,你通常会将本地文件转换为 File 对象(这是一个专门的 Blob)以便与 FormData 一起发送。
  • FormData:在 POST 请求中发送文件数据和其他表单字段的标准机制。
  • 异步特性:所有文件和网络操作都是异步的。示例使用 async/await 提高可读性。你也可以直接使用 Promise 或回调。
  • 错误处理:对于文件操作(例如,磁盘空间不足、权限被拒绝)和网络操作(例如,服务器宕机、无网络连接)都至关重要。实现健壮的 try/catch 块并检查响应状态。
  • 权限:cordova-plugin-file 插件本身处理 Android 的存储权限(如果需要,它会请求 WRITE_EXTERNAL_STORAGE,尽管较新 Android 版本上的分段存储改变了行为)。如果你遇到权限问题,请确保你的应用的 config.xml 和平台特定清单文件配置正确。可能需要用户交互来授予权限。
  • 内容安全策略 (CSP):确保你的 index.html 的 CSP(<meta http-equiv=“Content-Security-Policy” …>)允许连接到下载/上传服务器,在 connect-src 中配置。例如,connect-src ‘self’ https://via.placeholder.com https://jsonplaceholder.typicode.com;。
  • 大文件与进度:对于大文件,XMLHttpRequest 比 Fetch 提供更好的进度跟踪(onprogress 事件),尽管 Fetch 的上传流式传输正在改进。文件插件的 FileWriter 也提供了 onprogress 事件。
  • 服务器端实现:本教程仅涵盖客户端。你需要配置一个服务器端点来接受文件上传和提供文件下载。
  • 下载图片、视频、文档或数据更新以供离线使用。
  • 上传用户生成的内容,如照片、视频或数据备份。
  • 与远程服务器同步文件。

有关详细的 API 参考,请访问:Cordova File 插件 和 MDN 关于 Fetch API 和 FormData 的文档。