Skip to content

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.

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-file

Remember to rebuild your project after adding plugins: cordova build <platform>.

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>

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.

// In www/js/index.js
document.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);
}

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);
}
}

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);
}
}
  • cordova-plugin-file: Essential for resolving file system URLs (window.resolveLocalFileSystemURL), getting DirectoryEntry and FileEntry objects, reading, and writing files (createWriter, fileEntry.file() to get a File object). 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 / File Objects: Downloads are typically received as Blobs. For uploads, you’ll usually convert your local file into a File object (which is a specialized Blob) to send with FormData.
  • FormData: Standard mechanism for sending file data and other form fields in a POST request.
  • Asynchronous Nature: All file and network operations are asynchronous. The examples use async/await for 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/catch blocks and check response statuses.
  • Permissions: cordova-plugin-file itself handles Android’s storage permissions (it will request WRITE_EXTERNAL_STORAGE if needed, though scoped storage on newer Android versions changes behavior). Ensure your app’s config.xml and 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 in connect-src. E.g., connect-src 'self' https://via.placeholder.com https://jsonplaceholder.typicode.com;.
  • Large Files & Progress: For large files, XMLHttpRequest offers better progress tracking (onprogress event) than Fetch for downloads, though Fetch upload streaming is improving. The File plugin’s FileWriter also has onprogress events.
  • 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.
  • 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.