In this episode we'll download files with a custom name or the original name from the server, resume interrupted downloads, upload files via PUT, access FTP and SFTP, and make use of the progress bar.

In episode 6, you became skilled at sending and reading JSON from REST APIs — structured data in text form. But the real world is also full of raw files: binary installers, log archives, database backups, PDF documents. Downloading and uploading files is one of curl's earliest and most frequent uses — long before it became an API testing tool. This episode covers the correct download and upload commands, plus two features that save many people: resume and the progress bar.
The core of this episode is simple: download and upload are two directions of the same transfer. Both merely route data from a URL to a file (download) or from a file to a URL (upload). The difference lies in the direction of the data flow and the options used.
-o vs -Ocurl doesn't save download results to a file automatically — by default the response body is written to stdout (screen). To save it as a file, you need an output option. There are two choices that look similar but mean different things: -o and -O.
curl -o setup.deb https://example.com/downloads/package.deb-o setup.deb saves the file with a name you choose yourself — free to call it whatever you want. Meanwhile, -O (capital letter) saves with the original name from the server: curl takes the last part of the URL as the file name. The first suits when you want to organize files into specific names; the second fits when downloading many files without typing each name. The difference is as thin as capital vs lowercase letters, but choosing wrong means a file name that doesn't match expectations.
Tip
The only difference between -o and -O is how the destination file name is determined: -o needs an explicit name from you, -O guesses it from the URL. Rule of thumb — use -O when the file name on the server is already correct, use -o when you want to replace it or avoid a problematic name.
--remote-name-allWhen downloading several URLs at once, writing -o for each URL is tedious. The --remote-name-all flag makes curl apply the -O behavior (use original names) to all URLs at once:
curl --remote-name-all \
https://example.com/downloads/app-v1.tar.gz \
https://example.com/downloads/app-v2.tar.gzWith a single flag, all files are saved with their original names from each server. This is very practical when downloading a set of release artifacts or log files from several URLs at once.
-C -Large file downloads often get interrupted midway — network drops, laptop sleeps, or an unstable connection. Starting over from zero means wasting bandwidth. The -C - (continue) flag tells curl to resume from the last byte already downloaded:
curl -C - -O https://example.com/downloads/backup-2026.dbThe - after -C means "figure out the resume point yourself" — curl checks the size of the existing local file, then asks the server to send the remaining data starting from that offset. Servers that support HTTP Range will answer with code 206 Partial Content. If the local file turns out to be complete, curl will tell you there's nothing left to download. This feature makes downloading giant files feel far safer.
-TUpload is the opposite direction of download. The -T (upload) flag sends the file contents to the destination URL. For HTTP and HTTPS URLs, curl sends it as a PUT request:
curl -T build/app.jar https://files.example.com/release/app.jarThe combination of -T build/app.jar with the destination URL above is sufficient — no need to write -X PUT, because -T automatically picks the PUT method for HTTP URLs. This pattern is widely used to deploy artifacts to object storage (for example S3 via presigned URLs), artifact registries, or WebDAV servers. For more secure uploads, use SFTP or FTP with credentials as below.
curl isn't just an HTTP client — it's also a built-in FTP and SFTP client. URLs with the ftp:// and sftp:// schemes are supported directly, and authentication works through -u with the user:pass format:
curl -u budi:rahasia -T laporan.pdf \
ftp://files.example.com/reports/laporan.pdfcurl -u budi:rahasia -O \
sftp://files.example.com/backup/arsip-2026.tgzAn important difference to remember: FTP sends credentials and data in plain text (unencrypted), so it's only safe on trusted networks. SFTP, in contrast, runs over SSH and encrypts all communication — that's the right choice for sensitive files. Also note that for SFTP, the path in the URL is an absolute path on the destination server.
Warning
Never put user:pass credentials directly in the command history for production servers. For FTP or SFTP you use frequently, use a curl config file (covered in episode 8), or switch to SSH keys for SFTP — safer and doesn't leak through shell logs.
By default, curl shows a progress meter as a percentage that often looks messy in the terminal when used in pipelines or logs. The --progress-bar flag simplifies it into a clean single-line progress bar:
curl --progress-bar -O https://example.com/downloads/package.debThe output is a line of # characters growing with download progress, complete with transfer speed and estimated time remaining. For scripts downloading many files that want concise logs, --progress-bar is far friendlier than the multi-line default meter.
Not every file needs to be saved to disk first. Because curl writes the body to stdout by default, you can pipe it directly to another program — the classic pattern is downloading an archive and extracting it without an intermediate file:
curl -sL https://example.com/releases/app.tar.gz | tar -xzf -This pipeline downloads the archive and extracts it in a single step — without leaving a .tar.gz file on disk. It saves space and makes scripts more concise. The same pattern applies to other data: curl -s URL | jq from episode 6, or curl -s URL | sha256sum to verify a checksum.
Tip
When streaming into a pipeline, don't forget -s to suppress the progress meter — otherwise the progress bar will mix with the output of the program you're piping into and corrupt the data being passed through. -L is also important for following redirects, for example from shortlinks or CDNs.
Episode 7 equips you with file transfer capabilities: -o for custom names and -O for the original server name, --remote-name-all for many URLs at once, -C - for resuming interrupted downloads, -T for upload via PUT, FTP and SFTP access with -u, and --progress-bar for clean feedback.
The core thing to remember: download and upload are two sides of the same transfer, and curl handles both with the same URL schemes. Understand when to use -o versus -O, and always choose SFTP over FTP for sensitive data.
In the next episode 8, we'll cover configuration — how to store your favorite options in the ~/.curlrc config file, use --config for specific projects, and how curl reads environment variables such as proxy. See you!