Uploads a single file, or all parquet files in a directory of (partitioned) parquet files keeping the
directory structure, to a bucket that has to exist (see create_r2_bucket()). Directory uploads write a
manifest (_manifest.json), and all uploads keep the existing manifests of the dataset current.
Arguments
- path
local file, or directory of (partitioned) parquet files.
- destination
destination as
r2://bucket/pathors3://bucket/path:for a file: the path of the file, or with a trailing slash (
/), or just the bucket, the directory it goes into under its basename;for a directory: the directory its content goes into, which has to be a directory in the bucket, not the bucket itself.
- purge_cache
TRUE(default) orFALSE: purge the uploaded files from the Cloudflare cache of the bucket's custom domains. Ignored for S3.- remove_stale
what happens to parquet files below
destinationthat are not in the uploaded directory:FALSE(default): they are left in place.TRUE: they get removed before the upload, so that the destination mirrors the directory, e.g. when a dataset got rebuilt without a partition or file. Other files in the directory and files abovedestinationare never touched; manifests of directories belowdestinationlose the removed files, and go when nothing is left.
Details
Each file goes up in a single request and is read into memory for that, so a file has to fit into memory,
and the 5 GB limit of single requests applies; split larger data into partitions with
data_to_parquet(). File names with *, ? or [ are refused, since DuckDB reads them as patterns when the
files are queried. If an R2 bucket is served via a custom domain, the uploaded files get purged from the
Cloudflare cache, see the Credentials section.
A single file can be of any type, like a JSON index or a README that gets published next to a dataset. It
goes up with the content type for its extension, as mime::guess_type() guesses it (application/json for
.json, text/csv for .csv, and so on), so that browsers and other clients handle it correctly; parquet
files and unknown extensions are sent as application/octet-stream. remove_parquet() removes such files
again. _manifest.json files are reserved for manifests and can't be uploaded, use write_manifest() to
write them.
Manifests
A manifest lists the parquet files below its directory with their size, etag, modification time and footer
metadata, see write_manifest() for the format. It lets cache_parquet() and parquet_tbl() read a directory
from a public URL, where the files can't be listed, and lets other clients read the dataset's metadata
without the parquet footers.
The manifest of the destination of a directory upload is built from a listing of the directory, so it also repairs a manifest that fell behind.
Since a manifest lists all parquet files below its directory, the existing manifests of the directories above the destination get updated too, and for directory uploads those below it, so that uploading a single file or a new partition into a dataset keeps its manifest current. These get patched from what they record, without listing their directories: the uploaded and removed files are swapped in and out, and the metadata of the uploaded files is read from the local files.
write_manifest()rebuilds a manifest from a listing when files were changed by other means.Manifests are written conditionally, so that an upload running at the same time can't overwrite an update, and the update starts over from the current manifest when that happens. The upload warns, after the files are up, when a manifest could not be updated this way.
Uploading a single file never creates a manifest. A directory can't be uploaded to the root of the bucket, since its manifest would list the whole bucket and
remove_stalewould empty it;write_manifest()writes a manifest of a whole bucket on request.
Credentials
Credentials come from environment variables, e.g. set in .Renviron, read at call time. Public URLs and
local paths need none.
Paths on R2 (r2://bucket/path):
R2_ACCOUNT_ID: the R2 account id.R2_ACCESS_KEY_IDandR2_SECRET_ACCESS_KEY: the access key id and secret access key of an R2 API token with access to the bucket.R2_API_TOKEN, optional: the value of an API token with Zone Cache Purge permission (seecreate_r2_bucket()). Files that change on R2 then get purged from the Cloudflare cache of the bucket's custom domains, so that the new versions are served right away; the purge is skipped without it.R2PARQUET_PURGE_ORIGINS, optional: the origins of web pages reading the bucket from a browser, comma separated, likehttps://example.com,https://www.example.com. Cloudflare caches the responses to their requests separately perOrigin, which a purge of the plain URL misses, so each changed file also gets purged once per origin; copies cached for origins not listed are refreshed when the edge TTL runs out. This only concerns web apps, whose browsers send theOriginheader; requests without one, like those of this package, DuckDB or curl, get the plain URL's copy, which is always purged.
Paths on S3 (s3://bucket/path), on Amazon S3 or another S3 compatible service like MinIO, use the
variables AWS tools use:
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY, andAWS_SESSION_TOKENfor temporary credentials.AWS_REGIONorAWS_DEFAULT_REGION: the region, defaultus-east-1.AWS_ENDPOINT_URL_S3orAWS_ENDPOINT_URL: the endpoint of other S3 compatible services, likehttp://localhost:9000, which get addressed with path style URLs (endpoint/bucket/key).R2PARQUET_S3_URL_STYLE:pathorvhost, overrides the URL style.
AWS profiles and single sign-on are not read, aws configure export-credentials --format env prints the
variables for them.
