FILE_ADDED is not file-ready: a safer product-photo pipeline
An import worker can see a path before its producer has finished writing the file. In a product-photo workflow, that can look like a broken thumbnail or a failed uploadβeven when the capture itself was fine. I work on C
An import worker can see a path before its producer has finished writing the file. In a product-photo workflow, that can look like a broken thumbnail or a failed uploadβeven when the capture itself was fine.
I work on CartVisual's product-creative guides. This tutorial separates three things that often get bundled together: a camera event, a completed host transfer, and an image that is actually usable.
A camera event is the beginning of a handoff
In the official libgphoto2 tethering example, the GP_EVENT_FILE_ADDED branch obtains the camera file path and then calls gp_camera_file_get to download it. That sequence is a useful boundary: receiving an event does not, by itself, prove that your downstream host-side processing has finished.
Do not copy the example's camera-file deletion into a production workflow without a separate retention decision. Keep the original available while you verify the transfer.
For an application you control, give the producer ownership of the completion signal:
- Assign a capture/job ID and the expected destination.
- Transfer into a temporary name that consumers ignore.
- Check the transfer result and finish the write.
- Publish a ready name or explicit ready event only after success.
- Decode the resulting image and check its association with the intended product.
The final step matters: a complete file can still be the wrong photograph.
A small, reproducible Node demonstration
This is a synthetic binary example, not a camera test or a valid image. It deliberately pauses between two awaited writes so you can inspect the partial file. It leaves its temporary directory available for inspection.
import { mkdtemp, open, stat, rename, readFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
const directory = await mkdtemp(path.join(tmpdir(), 'file-readiness-'));
const pending = path.join(directory, 'sample.bin.part');
const ready = path.join(directory, 'sample.bin');
const expected = Buffer.alloc(32768, 0x5a);
const handle = await open(pending, 'wx');
await handle.write(expected.subarray(0, 4096));
console.log('visible before completion:', (await stat(pending)).size);
await handle.write(expected.subarray(4096));
await handle.close();
await rename(pending, ready);
const actual = await readFile(ready);
console.log('completed bytes:', actual.length);
console.log('matches expected bytes:', actual.equals(expected));
Our local Windows run using Node v24.16.0 recorded:
visible before completion: 4096
completed bytes: 32768
matches expected bytes: true
This demonstrates the difference between path existence and completed bytes in this controlled example. It does not measure how often a real camera pipeline fails. The two writes here are awaited in order; a real transfer must also handle partial writes, errors and interrupted jobs. Use a proper stream or full-write loop for arbitrary input rather than assuming one low-level write always consumes every byte.
The temporary and ready names above are in the same directory. Treat rename as one part of a producer/consumer protocol, not a universal promise of durable storage or identical behavior across network filesystems. See the official Node file-system documentation for the API. Your actual deployment still needs its own crash, filesystem and recovery checks.
A stable size is only a fallback clue
If you do not control the producer, two equal size observations can help identify a candidate file, but a paused writer can also appear stable. Prefer the transfer API's successful completion result or a documented producer event. Add a timeout and retry state instead of letting an import sit indefinitely at βprocessing.β
Keep these outcomes separate in logs:
| Check | What it tells you | What it cannot establish |
|---|---|---|
| Camera event received | An event and camera path were reported | Host transfer complete |
| Transfer succeeded | The transfer API reported success | Image decodes correctly |
| Decoder accepted the file | The tested decoder could read it | Correct SKU, angle or approved label |
| Product review passed | The asset matches the review criteria | Every future export or storefront version is correct |
For failures, retain the job ID, event time, transfer result, final path, observed size and decoder result. Those fields make a retry explainable without quietly replacing the evidence.
Put the technical check beside the photo plan
The CartVisual file-readiness guide and editable worksheet cover the camera-event/host-transfer distinction in more detail. For the non-code side, the free product photography shot-list builder helps record intended products, angles and references before capture.
These are planning and review resources. This tutorial does not claim that CartVisual connects to a camera SDK, performs tether transfers or automatically verifies your local files.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.