Cordova - File Transfer
Cordova - File Operations (Download and Upload)
Section titled “Cordova - File Operations (Download and Upload)”The cordova-plugin-file-transfer plugin, previously used for this purpose, is now deprecated. Modern Cordova applications should use standard web APIs like the Fetch API or XMLHttpRequest for network operations (downloading and uploading files), in conjunction with the cordova-plugin-file for accessing and managing files on the device’s filesystem.
This tutorial demonstrates how to download a file from a server and upload a file from the device using these modern approaches.
Step 1 - Install Necessary Plugins
Section titled “Step 1 - Install Necessary Plugins”You’ll need the cordova-plugin-file to interact with the device’s file system. The Fetch API is a built-in browser feature, also available in Cordova’s WebView.
cordova plugin add cordova-plugin-fileRemember to rebuild your project after adding plugins: cordova build <platform>.
Step 2 - Prepare the UI
Section titled “Step 2 - Prepare the UI”In www/index.html, add buttons for downloading and uploading, and a way to select a file for upload (though for simplicity, we’ll first demonstrate uploading a pre-existing or newly created file). Add an area for status messages.
<body> <h1>File Operations Demo</h1>
<h2>Download File</h2> <button id="downloadFileBtn">DOWNLOAD IMAGE</button>
<h2>Upload File</h2> <!-- For a real app, you'd use <input type="file"> or cordova-plugin-camera to get a file URI --> <button id="createAndUploadFileBtn">CREATE & UPLOAD TEXT FILE</button>
<div id="statusMessages" style="margin-top: 20px; border: 1px solid #ccc; padding: 10px;"> Status messages will appear here... </div>
<script src="cordova.js"></script> <script src="js/index.js"></script></body>Step 3 - Implement JavaScript Logic
Section titled “Step 3 - Implement JavaScript Logic”In www/js/index.js, within onDeviceReady, set up event listeners and the core logic. All file operations depend on cordova-plugin-file being ready, which is ensured by deviceready.
Helper Function for Status Updates
Section titled “Helper Function for Status Updates”// In www/js/index.jsdocument.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() { console.log('Device is ready. File and Fetch APIs available.'); 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);}Part A: Downloading a File
Section titled “Part A: Downloading a File”This function downloads an image from a placeholder URL and saves it to the app’s data directory.
async function downloadFileExample() { const fileURL = "https://via.placeholder.com/300/09f/fff.png?text=CordovaDownload"; // Example image URL const fileName = "downloadedImage.png"; // Target directory: cordova.file.dataDirectory is persistent and private to the app. // Other options: cordova.file.cacheDirectory (for temporary cache files) const targetDirectory = cordova.file.dataDirectory;
updateStatus(`Starting download of ${fileName} from ${fileURL}...`);
try { const response = await fetch(fileURL); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const blob = await response.blob(); updateStatus('File fetched as blob.');
// Get DirectoryEntry for the target directory const dirEntry = await new Promise((resolve, reject) => { window.resolveLocalFileSystemURL(targetDirectory, resolve, reject); }); updateStatus(`Target directory resolved: ${dirEntry.toURL()}`);
// Get FileEntry for the new file const fileEntry = await new Promise((resolve, reject) => { dirEntry.getFile(fileName, { create: true, exclusive: false }, resolve, reject); }); updateStatus(`File entry created/accessed: ${fileEntry.toURL()}`);
// Write the blob to the file await new Promise((resolve, reject) => { fileEntry.createWriter((fileWriter) => { fileWriter.onwriteend = () => { updateStatus(`File ${fileName} downloaded successfully to ${fileEntry.toURL()}`); resolve(); }; fileWriter.onerror = (e) => { updateStatus(`Failed to write file: ${e.toString()}`, true); reject(e); }; fileWriter.write(blob); }, reject); });
} catch (error) { updateStatus(`Download error: ${error.message || error}`, true); }}Part B: Creating and Uploading a File
Section titled “Part B: Creating and Uploading a File”This function first creates a simple text file in the app’s data directory and then uploads it to a test server URL. In a real application, you would likely get the file URI from a file picker (<input type="file">) or another plugin like cordova-plugin-camera.
async function createAndUploadFileExample() { const textContent = `Hello from Cordova! Timestamp: ${new Date().toISOString()}`; const fileName = "uploadMe.txt"; const fileDirectory = cordova.file.dataDirectory; const uploadUrl = "https://jsonplaceholder.typicode.com/posts"; // Example upload endpoint (will return what you send)
updateStatus(`Attempting to create and upload ${fileName}...`);
try { // 1. Create the file and write content 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(`File entry for upload: ${fileEntry.toURL()}`);
await new Promise((resolve, reject) => { fileEntry.createWriter((fileWriter) => { fileWriter.onwriteend = () => { updateStatus(`Content written to ${fileName}.`); resolve(); }; fileWriter.onerror = (e) => { reject(new Error(`Failed to write to file: ${e.toString()}`)); }; const blob = new Blob([textContent], { type: 'text/plain' }); fileWriter.write(blob); }, (err) => reject(new Error(`FileWriter creation failed: ${err}`))); });
// 2. Read the file as a Blob for upload const fileToUpload = await new Promise((resolve, reject) => { fileEntry.file(resolve, reject); }); // fileToUpload is a File object (which is a Blob)
// 3. Upload the file using Fetch API with FormData const formData = new FormData(); formData.append('file', fileToUpload, fileName); // 'file' is the field name expected by the server formData.append('description', 'Cordova file upload test');
updateStatus(`Uploading ${fileName} to ${uploadUrl}...`); const response = await fetch(uploadUrl, { method: 'POST', body: formData, // Headers might be needed for your server, e.g., for authentication // headers: { // 'Authorization': 'Bearer YOUR_TOKEN_HERE' // } });
if (!response.ok) { const errorText = await response.text(); throw new Error(`Upload HTTP error! Status: ${response.status}, Message: ${errorText}`); }
const responseData = await response.json(); // Assuming server responds with JSON updateStatus(`Upload successful! Server response: ${JSON.stringify(responseData)}`); console.log('Server response:', responseData);
} catch (error) { updateStatus(`Upload error: ${error.message || error}`, true); }}Key Concepts and Considerations
Section titled “Key Concepts and Considerations”cordova-plugin-file: Essential for resolving file system URLs (window.resolveLocalFileSystemURL), gettingDirectoryEntryandFileEntryobjects, reading, and writing files (createWriter,fileEntry.file()to get aFileobject). Common directories include:cordova.file.dataDirectory: Persistent, app-private storage.cordova.file.cacheDirectory: For cache files that the OS can delete.cordova.file.externalDataDirectory: External storage (e.g., SD card) app-private directory.- Refer to plugin docs for a full list and their meanings.
Fetch API: Used for the actual network request.fetch(url)for GET (download),fetch(url, { method: 'POST', body: formData })for POST (upload).Blob/FileObjects: Downloads are typically received asBlobs. For uploads, you’ll usually convert your local file into aFileobject (which is a specializedBlob) to send withFormData.FormData: Standard mechanism for sending file data and other form fields in aPOSTrequest.- Asynchronous Nature: All file and network operations are asynchronous. The examples use
async/awaitfor readability. You can also use Promises directly or callbacks. - Error Handling: Crucial for both file operations (e.g., disk full, permissions denied) and network operations (e.g., server down, no internet). Implement robust
try/catchblocks and check response statuses. - Permissions:
cordova-plugin-fileitself handles Android’s storage permissions (it will requestWRITE_EXTERNAL_STORAGEif needed, though scoped storage on newer Android versions changes behavior). Ensure your app’sconfig.xmland platform-specific manifests are correctly set up if you encounter permission issues. User interaction might be required to grant permissions. - Content Security Policy (CSP): Ensure your
index.html’s CSP (<meta http-equiv="Content-Security-Policy" ...>) allows connections to the download/upload servers inconnect-src. E.g.,connect-src 'self' https://via.placeholder.com https://jsonplaceholder.typicode.com;. - Large Files & Progress: For large files,
XMLHttpRequestoffers better progress tracking (onprogressevent) thanFetchfor downloads, thoughFetchupload streaming is improving. TheFileplugin’sFileWriteralso hasonprogressevents. - Server-Side Implementation: This tutorial only covers the client-side. You need a server endpoint configured to accept file uploads and serve files for download.
Practical Applications
Section titled “Practical Applications”- Downloading images, videos, documents, or data updates for offline use.
- Uploading user-generated content like photos, videos, or data backups.
- Syncing files with a remote server.
For detailed API references, visit: Cordova File Plugin and MDN for Fetch API and FormData.