PHP File Upload
Upload Files From PHP With curl or Guzzle
Upload files from PHP with curl. No SDK, REST API, CDN delivery, prepaid credits.
The aws-sdk-php package is a heavyweight Composer dependency for what is, at heart, a few HTTP requests — and on shared hosting or a lean WordPress install you may not want it at all. files.link works with the curl extension that ships with PHP, or with Guzzle if you already have it, and the upload is three calls. Request a presigned URL by POSTing metadata to /v1/files/{folderId} with your API key in the Authorization header (raw key, no Bearer prefix); PUT the bytes to the returned url; POST /v1/files/confirm-upload with the file id. With the bundled curl extension and no Composer dependency at all:
<?php
$api = 'https://api.files.link';
$key = getenv('FILESLINK_KEY');
$folder = 'your-folder-id';
$path = 'invoice.pdf';
// 1. request a presigned PUT URL
$ch = curl_init("$api/v1/files/$folder");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Authorization: $key", 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['filesMetadata' => [['name' => 'invoice.pdf']]]),
]);
$meta = json_decode(curl_exec($ch), true);
$slot = $meta['urls'][0];
// 2. PUT the file bytes
$ch = curl_init($slot['url']);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_PUT => true,
CURLOPT_INFILE => fopen($path, 'rb'),
CURLOPT_INFILESIZE => filesize($path),
]);
curl_exec($ch);
// 3. confirm
$ch = curl_init("$api/v1/files/confirm-upload");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Authorization: $key", 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['ids' => [$slot['id']]]),
]);
curl_exec($ch);Using CURLOPT_INFILE streams the file rather than loading it into a string, which keeps you under PHP's memory_limit for large uploads — the usual bottleneck in PHP file handling. In a framework you'd reach for the native client: Laravel's Http::withHeaders(['Authorization' => $key])->post(...) for the metadata and confirm steps and ->withBody(...)->put(...) for the bytes; Symfony's HttpClient or Guzzle work the same way.
On WordPress, wp_remote_post and wp_remote_request (which supports PUT) let a plugin fire the upload from a hook with the key stored in wp_options — handy for offloading media to a CDN-backed store without bundling an SDK. Confirmed public files come back as permanent CDN URLs; private files use signed URLs with a short expiry, chosen at upload with a flag. For files above ~100MB (up to 1TB) the multipart endpoint streams chunks so you never exceed memory_limit.
Billing is prepaid credits, so an over-eager batch job costs credits, not a runaway bill. Compared to aws-sdk-php you drop a large dependency and a credentials file for a contract that's a few curl_setopt lines. Related: the WordPress media storage page for that specific offload, and the cloud file storage API page for the full route reference.
Benefits With No Complexity
Global CDN delivery
Edge-cached worldwide
Signed-URL security
What You Get
Unlimited files
Unlimited storage
Public + Private storage
CDN ready links
Prepaid credits
More coming soon
How files.link Works
1. Upload
2. Copy
3. Use Anywhere
Why Developers Choose files.link
A Better Way to Store & Deliver Files
| AWS S3 (aws-sdk-php) | Cloudinary SDK | Google Cloud Storage | files.link | |
|---|---|---|---|---|
| Works with bundled curl (no Composer install) | ||||
| Smaller dependency footprint | ||||
| Streams via CURLOPT_INFILE (low memory) | ||||
| Laravel / Symfony / WordPress friendly | ||||
| Global CDN delivery included | ||||
| Multipart up to 1TB per file | ||||
| Prepaid credits, no surprise bills |
Calculate Your Needs
Storage
Egress
CDN Bandwidth
Upload Files from PHP
- Global CDN delivery
- Edge-cached worldwide
- Signed-URL security
- Unlimited files
- Unlimited storage
- Public + Private storage
- No subscription — prepaid credits keep spend predictable