ionic-team / ionic-team/capacitor

[Bug]: capacitor/http Upload progress not supported

Open
#8,241 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
16.7k
Forks
1.3k
Avg merge
3d 15h
Merged PRs (30d)
10

Description

### Capacitor Version

I would like to address an issue that was automatically closed. I am having the same problem.

https://github.com/ionic-team/capacitor/issues/6658

Currently, the Capacitor HTTP plugin does not emit upload progress events, so passing onUploadProgress in requests has no effect. This is not a bug in user code — it is a limitation of the plugin:
• iOS: Uses URLSession.shared without a delegate. Upload progress requires implementing urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:).
• Android: Uses RequestBody directly without a wrapper. Upload progress requires a custom RequestBody that reports bytes written.

By contrast, the Capacitor File Transfer plugin handles native upload progress correctly on both platforms.

Upload progress is critical for mobile apps that handle large files. Adding native support would allow the Capacitor HTTP plugin to:
1. Support progress bars and UI feedback during uploads.
2. Reach feature parity with web APIs like XHR and Axios.
3. Make Capacitor a complete solution for file uploads without relying on additional plugins.

Request: Please add native upload progress support on iOS and Android, and expose it via notifyListeners("uploadProgress") so developers can track upload progress natively.

### Platforms Affected

- [x] iOS
- [x] Android
- [ ] Web

### Current Behavior

I hooked up the onprogress event in my code to show the progress of downloading a file to the user. But enabling the @capacitor/http plugin the progress doesn't show up anymore. I only see the popup and when the downloading is done, it will show the file. When I disable the plugin, the progress is shown again.

### Expected Behavior

The user should see the progress of the download.

### Project Reproduction

```javascript
makeXMLRequest(method, params, headers, useDatabase=true, cancelFunc, progressFunc) {
const req = this.checkParams(params, headers, useDatabase);
if (req.error !== undefined && req.error !== null) {
return Promise.reject(req.error);
}

return new Promise((resolve, reject) => {
let fetch = new XMLHttpRequest();

if (cancelFunc !== undefined && cancelFunc !== null) {
cancelFunc.cancel = () => {
fetch.abort(null);
};
}
// open a connection
fetch.open("POST", req.host);
// set extra request headers
for (const key in req.headers) {
fetch.setRequestHeader(key, req.headers[key]);
}

fetch.onload = (oEvent) => {
if (fetch.status >= 200 && fetch.status < 300) {
// difference between @capacitor/http and the 'normal' XMLHttpRequest
// @capacitor/http returns already a JSON
const result = JSON.parse(fetch.response);
resolve(result.result);
} else {
reject(fetch.statusText);
}
};
// send progress data back
fetch.onprogress = (oEvent) => {
if (progressFunc !== undefined && progressFunc !== null) {
progressFunc(oEvent.loaded, oEvent.total);
}
};

fetch.onerror = (oEvent) => {
reject(fetch.statusText);
};
// abort the connection
fetch.onabort = (oEvent) => {
reject(false);
};
// send the data
fetch.send(JSON.stringify(
{method: method, params: req.params, id: req.uid}
));
});
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.