mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-23 21:56:57 +00:00
Add resilient PDF thumbnails to GUI uploads (#3809)
* Add resilient PDF thumbnails to GUI uploads * Keep GUI image thumbnails working with SDKs lacking the callback context The desktop loads puter.js from js.puter.com by default, and the SDK there predates the thumbnail callback context, so passing the PDF generator made every image upload lose its thumbnail until the SDK deploys. Fall back to the SDK's bundled image generator whenever the running SDK passes no usable context, and cover both paths in the unit and browser tests. * Ignore preparation failures that land after an upload is cancelled Cancelling during preparation already rejects the upload and fires the abort callback. If the step that was in flight then fails, such as a dropped directory that cannot be read, the error callback also fired and the GUI showed an upload error for an upload the user had just cancelled. Skip error reporting once preparation has been aborted. * Give each PDF thumbnail worker four seconds The per-PDF budget covers downloading PDF.js as well as rendering, and the first PDF of a session on a slower connection ran out of time before its assets had even loaded. Four seconds fits that first load on ordinary connections while staying under the five-second batch cap, so one stuck PDF still leaves the rest of the batch a chance.
This commit is contained in:
@@ -5,6 +5,8 @@ description: Store and manage data in the user's own cloud drive with Puter.js f
|
||||
|
||||
The Cloud Storage API lets you store and manage data in the cloud.
|
||||
|
||||
Local [uploads](/FS/upload/) can optionally generate browser image thumbnails or use a custom thumbnail callback. The callback can delegate to the built-in image generator and respond to upload cancellation. The Puter desktop additionally provides PDF previews without adding a PDF renderer to the SDK.
|
||||
|
||||
It comes with a comprehensive but familiar file system operations including write, read, delete, move, and copy for files, plus powerful directory management features like creating directories, listing contents, and much more.
|
||||
|
||||
With Puter.js, you don't need to worry about setting up storage infrastructure such as configuring buckets, managing CDNs, or ensuring availability, since everything is handled for you. Additionally, with the [User-Pays Model](/user-pays-model/), you don't have to worry about storage or bandwidth costs, as users of your application cover their own usage.
|
||||
|
||||
@@ -31,6 +31,9 @@ A set of key/value pairs that configure the upload process. The following option
|
||||
- `overwrite` (Boolean) - Whether to overwrite the destination file if it already exists. Defaults to `false`.
|
||||
- `dedupeName` (Boolean) - Whether to deduplicate the file name if it already exists. Defaults to `true`. Ignored when `overwrite` is `true`.
|
||||
- `createMissingParents` (Boolean) - Whether to create missing parent directories. Defaults to `false`.
|
||||
- `generateThumbnails` (Boolean) - Generate image thumbnails in the browser before uploading. Defaults to `false`. Unsupported files and generation failures are skipped.
|
||||
- `thumbnailGenerator` (Function) - Optional `(file, context) => string | undefined` callback (which may also return a promise), called once per file instead of the built-in image generator. Return a thumbnail data URL or URL, or `undefined` to skip. Exceptions are ignored. `context.defaultGenerator(file)` delegates to the built-in image generator; `context.signal` is an `AbortSignal` for upload preparation cancellation. Existing one-argument callbacks continue to work. A custom generator enables thumbnail preparation even when `generateThumbnails` is omitted.
|
||||
- `thumbnail` (String) - Optional thumbnail data URL or URL to use when a file has no generated thumbnail. Data URLs exceeding 2 MiB are discarded.
|
||||
|
||||
The following callbacks report on the upload as it runs. `operationId` identifies the upload, so a page running several uploads at once can tell them apart:
|
||||
|
||||
@@ -39,6 +42,8 @@ The following callbacks report on the upload as it runs. `operationId` identifie
|
||||
- `progress` (Function) - Called with `(operationId, progress)` as bytes are sent, where `progress` is a percentage between `0` and `100`.
|
||||
- `abort` (Function) - Called with `(operationId)` if the upload is aborted.
|
||||
|
||||
Cancelling through the `init` request handle during thumbnail preparation rejects with `{ code: 'upload_aborted', message: 'Upload aborted.' }` and prevents the upload from starting. Custom generators should stop their work when `context.signal` aborts and impose their own time and resource budgets; the SDK awaits thumbnail preparation before sending files.
|
||||
|
||||
```js
|
||||
puter.fs.upload(items, './uploads', {
|
||||
progress: (operationId, progress) => {
|
||||
@@ -68,6 +73,22 @@ On `nodejs` and `workers`, where the upload goes through an older batch endpoint
|
||||
|
||||
Directory uploads (dropped directory entries, or `createFileParent`) are supported on `websites` and `apps`. On `nodejs` and `workers` the upload goes through an older batch endpoint that cannot create the directory tree, so a directory upload rejects with `batch_upload_failed`; create the directories with [`puter.fs.mkdir()`](/FS/mkdir/) and upload the files into them instead.
|
||||
|
||||
## Thumbnails
|
||||
|
||||
The built-in generator handles browser-decodable images. PDF rendering is provided separately by the Puter desktop; PDF.js is not included in the SDK. Apps can supply their own renderer through `thumbnailGenerator` and delegate other files to `context.defaultGenerator`:
|
||||
|
||||
```js
|
||||
const file = new File(['Hello!'], 'hello.txt', { type: 'text/plain' });
|
||||
await puter.fs.upload(file, './', {
|
||||
thumbnailGenerator: async (file, { defaultGenerator, signal }) => {
|
||||
if (signal.aborted) return undefined;
|
||||
return defaultGenerator(file);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
When using signed uploads, a separate thumbnail transfer that fails or exceeds five seconds is skipped and the original file still uploads. Explicit upload cancellation still stops the upload. Errors transferring the original file continue to reject normally.
|
||||
|
||||
## Examples
|
||||
|
||||
<strong class="example-title">Upload a file from a file input</strong>
|
||||
|
||||
@@ -97,6 +97,21 @@ All per minute unless stated:
|
||||
|
||||
Signed-URL routes have no session to key on, so they are bounded per network rather than per account: 3,000 reads/min, 600 writes/min, 60 concurrent.
|
||||
|
||||
The Puter desktop generates PDF upload thumbnails locally with these best-effort budgets. Exceeding them skips the preview and does not reject the original file upload:
|
||||
|
||||
| PDF thumbnail preparation | Limit |
|
||||
| --- | --- |
|
||||
| Input PDF size | 20 MiB |
|
||||
| Active PDF renderers per desktop page | 1 |
|
||||
| Preparation per upload, including queued PDFs | 5 seconds from the first eligible PDF |
|
||||
| Worker lifetime per PDF, including asset loading and cleanup | 4 seconds |
|
||||
| Embedded image or intermediate canvas area | 4,194,304 pixels |
|
||||
| Image resize budget passed to PDF.js | 16 MiB |
|
||||
| Output | First page, at most 128 × 128 pixels, preserving aspect ratio |
|
||||
| Thumbnail payload | 2 MiB |
|
||||
|
||||
The SDK allows five seconds for each separate signed thumbnail transfer. A failed or timed-out thumbnail transfer is skipped; explicit upload cancellation and failures transferring the original file still stop the upload. These are preview budgets, not upload file-size limits. The PDF renderer's memory budgets do not constitute a hard limit on total browser-process memory.
|
||||
|
||||
### WebDAV
|
||||
|
||||
The `dav` host authenticates each request itself, so its limits are bounded per network rather than per account: **600 requests/min** and **10 concurrent**, one ceiling for everyone.
|
||||
|
||||
Reference in New Issue
Block a user