Downloading a file in OkHttp means streaming ResponseBody, choosing a sink, and closing resources correctly. In JQuickCurl, a download is just another curl command: add --output './download/report.pdf' (or -o) and the executor writes the response bytes to that path for you. No InputStream juggling, no try-with-resources plumbing in your business code.
This post covers:
The
--outputone-liner that saves straight to disk.Returning
byte[]and saving in Java for full control.Combined approaches and directory-handling notes.
A runnable demo against a real image endpoint.
Approach 1: Let the Command Write the File
The cleanest option. Add --output <path> to the curl command; the file appears at the path when the call returns.
import com.github.paohaijiao.anno.JCurlCommand;
import com.github.paohaijiao.domain.req.JQuickCurlReq;
public interface DownloadApi {
@JCurlCommand("curl -X GET 'https://httpbin.org/image/png' "
+ "--output './download/diagram.png'")
byte[] downloadPng(JQuickCurlReq request);
}
import com.github.paohaijiao.executor.JCurlInvoker;
public class DownloadDemo {
public static void main(String[] args) throws Exception {
DownloadApi api = JCurlInvoker.createProxy(DownloadApi.class);
byte[] bytes = api.downloadPng(new JQuickCurlReq());
java.nio.file.Path file = java.nio.file.Paths.get("./download/diagram.png");
System.out.println("Saved " + bytes.length + " bytes to "
+ file.toAbsolutePath());
// -> Saved NNNN bytes to .../download/diagram.png
}
}
Two things happen: the executor writes ./download/diagram.png, and (because the method still declares byte[]) you also receive the bytes — handy for checksumming or counting without an extra read. The directory must exist; JQuickCurl writes the file but does not create parent folders for you.
Approach 2: Return the Bytes and Save in Java
If the storage location is business logic (a database column, an S3 bucket, a temp dir chosen per tenant), skip --output and save the byte[] yourself:
public interface DownloadApi {
@JCurlCommand("curl -X GET 'https://httpbin.org/bytes/4096'")
byte[] downloadBytes(JQuickCurlReq request);
}
byte[] bytes = api.downloadBytes(new JQuickCurlReq());
java.nio.file.Files.write(
java.nio.file.Paths.get("./download/random-4k.bin"), bytes);
Return types handled by the converter include byte[], and JResult for raw access. Choose by payload size:
| Payload | Return type |
|---|---|
| Small / needs random access | byte[] |
| Medium, file target known in command |
--output in command (any return) |
| Need metadata too | JResult |
Directory Setup and Path Conventions
Pre-create the target directory (e.g.,
./download) before the first call.Prefer forward slashes —
--output './download/report.xlsx'is portable.On Windows, absolute paths like
'd://test//piett.xlsx'are the spelling used in the project's own XML samples; Java strings will treat\as escapes, so/is the low-friction choice.For versioned downloads, inject the file name via a variable:
@JCurlCommand("curl -X GET 'https://api.example.com/files/${fileId}' "
+ "-H 'Accept: application/octet-stream' "
+ "--output './download/${fileId}'")
byte[] downloadById(JQuickCurlReq request);
JQuickCurlReq req = new JQuickCurlReq();
req.put("fileId", "invoice-2026-03.pdf");
byte[] bytes = api.downloadById(req);
// file lands at ./download/invoice-2026-03.pdf
Complete Runnable Demo
import com.github.paohaijiao.anno.JCurlCommand;
import com.github.paohaijiao.domain.req.JQuickCurlReq;
import com.github.paohaijiao.executor.JCurlInvoker;
public interface MediaApi {
// Image with a header check to prove headers are respected
@JCurlCommand("curl -X GET 'https://httpbin.org/image/jpeg' "
+ "-H 'Accept: image/jpeg' "
+ "--output './download/photo.jpg'")
byte[] grabPhoto(JQuickCurlReq request);
}
class SaveToDiskDemo {
public static void main(String[] args) throws Exception {
java.nio.file.Files.createDirectories(java.nio.file.Paths.get("./download"));
MediaApi api = JCurlInvoker.createProxy(MediaApi.class);
byte[] bytes = api.grabPhoto(new JQuickCurlReq());
System.out.println("Received " + bytes.length + " bytes");
// Then verify the file on disk:
long onDisk = java.nio.file.Files.size(
java.nio.file.Paths.get("./download/photo.jpg"));
System.out.println("On disk : " + onDisk + " bytes");
}
}
Summary
Downloads collapse into one declarative flag: --output './path' writes the response to disk, while return types byte[] keep the memory footprint in your control. Combined with ${...} variables for file names, JQuickCurl handles the whole download lifecycle with the syntax every engineer already knows.
Code and docs live at dromara/jquick-curl. Post 11 moves from per-request concerns to the process level: global tuning of connection pools, timeouts, retries, and redirects.
Top comments (0)