Zhupi222
HomeAboutProjectsTechnical writingContact information

Asset Sync on Desktop Devices: First Full Download, Incremental Threshold, and Local Fallback

2026-07-18T00:00:00.000Z约9分钟
How a desktop asset path grew from one ZIP download into incremental updates and disconnected fallback, including the black-screen risk that changed replacement order.
Electron资源同步离线运行项目复盘

When remote Recipes first arrived, their JSON could reach the device while the images and videos they referenced remained on the server. Configuration was ready before its files, leaving the page with local URLs that did not exist. Asset synchronization entered the project to close that gap.

The first version only placed a complete resource bundle on disk during startup. Incremental updates, fallback after network failure, and detailed initialization progress came later as devices needed to keep running on unstable connections. The current source shows that sequence and the gaps still left open.

The cause: remote configuration referenced local files

Tokens contain filenames. An asset resolver converts a name into static://recipe-assets/{projectId}/{filename}, and Electron reads it from the user-data directory. Components do not need Windows paths, and videos can be streamed with Range requests.

That design creates one startup requirement: the project directory has to be ready before remote tokens activate. The first synchronization change touched main, renderer, and the loading UI, adding more than five hundred lines. Its job was direct—download a ZIP, report progress, extract it locally, then tell the provider that remote files were available.

The first version established a baseline with a full ZIP

Without local sync state, the device requests a complete ZIP URL, MD5, and update time. Missing metadata fails initialization. A clean installation has no older files to compare, so the code does not attempt to reconstruct a complete set from the incremental endpoint.

The ZIP downloads into the operating-system temporary directory. MD5 is calculated after the stream closes, and extraction begins only when it matches. Progress moves through download, verify, and extract. A successful run stores updatedAt for the projectId in recipe-sync-state.json.

That gave new devices a working baseline, but a full package was expensive for small changes. Incremental synchronization followed as the asset set grew.

Incremental updates use updatedAt

With an updatedAt value, the client requests files changed after that time. Each item includes a URL, MD5, and a new update time. The downloader derives the filename, writes a .tmp file beside the destination, verifies it, and renames it into place.

const destPath = path.join(targetDir, filename); const tmpPath = destPath + '.tmp'; await downloadFile(asset.url, tmpPath, signal); const actualMd5 = await computeFileMd5(tmpPath); if (actualMd5 !== asset.md5) { fs.unlinkSync(tmpPath); throw new Error('MD5 mismatch'); } fs.renameSync(tmpPath, destPath);

The temporary file keeps components from reading an incomplete download. MD5 catches transfer damage, and a failed check removes the temporary file. There are no version directories in this path; a verified incremental file enters the current directory.

The source also contains a concrete threshold. More than 20 changed files sends the run back to the full ZIP path. That number reflects the request-versus-bundle trade-off in this application. updatedAt is saved only after success, so a failed batch is requested again next time.

Disconnected startup was added later

When semi-offline operation became necessary, asset sync moved into the common app-init sequence. The orchestrator returns synced, fallback-local, aborted, or failed.

A network or API failure becomes fallback-local when the project directory already exists. Initialization records the skipped sync and continues. The same failure on a clean installation returns failed.

This covers a device that worked yesterday and lost connectivity today. It does not validate every local file or retain a previous-version tree. fallback-local currently means that the existing directory is available for continued use.

The renderer adds another safeguard. Before initDone, the provider uses bundled assets under public. The base UI can mount with its default Recipe even when project synchronization fails.

A black-screen risk changed replacement order

A separate static-resource path once cleared the live directory before extracting the replacement ZIP. An interrupted extraction left a partial set and could produce a black screen. The later fix extracted into a sibling temporary directory first and replaced the destination after extraction. Keeping the temporary directory on the same filesystem made rename reliable.

Recipe full sync now also downloads, verifies, and extracts before removing targetDir and renaming the candidate. It avoids extracting over live files. A short gap remains between removal and rename, and no previous version is retained. That detail still matters beyond the “atomic swap” comment in the source.

The result

The current path covers the device states encountered most often: a clean installation uses a full package, routine runs fetch changes, large batches return to full sync, damaged transfers do not replace targets, and a previously synchronized device can start without the network. Sync state and logs also show which path ran.

The remaining gaps are clear. Incremental responses do not remove deleted remote files, an existing local bundle is not fully checked during startup, and MD5 verifies integrity only. Version directories, delayed cleanup, and startup verification are the next practical improvements.

The code grew from one ZIP downloader into this sequence because of problems devices could encounter in operation. It is still smaller than a complete asset release system, and every fallback path has a specific reason in the project history.

PreviousLeetCode 567 Optimization Journey: From Brute Force to Sliding WindowNextWhy We Moved Deployment Differences into a Recipe System

Related posts

Why We Moved Deployment Differences into a Recipe System
2026-07-21T00:00:00.000Z约8分钟
Electron前端架构配置系统