This workflow inventories the File Cabinet through the authenticated NetSuite UI, then downloads the exact folder links found in that UI through bounded, authenticated streaming requests.
The displayed folder size is useful for scheduling but not for deciding whether a transfer is complete. A downloaded folder is a ZIP archive, so its transferred size can differ from the uncompressed size displayed by NetSuite.
A folder is marked complete only when:
- The HTTP response finishes.
Content-Lengthmatches when NetSuite supplies it.- The ZIP central directory can be read.
- The temporary
.partfile is atomically renamed to its final name.
Interrupted downloads remain pending or failed in SQLite and are safe to
retry.
Use the same Python virtual environment as the existing crawler:
pip install -r requirements-file-cabinet.txt
python file_cabinet_downloader.py scan --profile-dir "C:\NetSuiteChromeProfile"Review:
file_cabinet_manifest.csvfile_cabinet_state.sqlite3- The reported count, which should be 4,432 folders.
Then run a conservative ten-folder download test:
python file_cabinet_downloader.py download `
--profile-dir "C:\NetSuiteChromeProfile" `
--workers 1 `
--max-downloads 10 `
--in-flight-gib 1.5 `
--large-threshold-gib 0.75 `
--reserve-disk-gib 10The persistent Chrome profile can be omitted when the existing crawler.py
module exposes its normal is_logged_in(driver) and login(driver) functions.
Start with:
--workers 2--in-flight-gib 1.5--large-threshold-gib 0.75--reserve-disk-gib 10--verify structure
This permits two ordinary transfers when their combined displayed sizes fit within approximately 1.5 GiB. A folder of 0.75 GiB or more runs alone.
Do not begin at six workers. Increase to three only after at least 100 folders complete without NetSuite throttling, login expiry, increased server latency, or local disk pressure.
Inventory without downloading:
python file_cabinet_downloader.py scanInventory and then download:
python file_cabinet_downloader.py allResume pending folders:
python file_cabinet_downloader.py downloadRetry folders marked failed:
python file_cabinet_downloader.py download --retry-failedShow status and refresh the CSV manifest:
python file_cabinet_downloader.py statusPerform full ZIP CRC verification:
python file_cabinet_downloader.py download --verify crcCRC verification reads all decompressed members and therefore increases local CPU and disk work. The default structural validation is more appropriate during the main 93 GB transfer. CRC can be run for high-value folders or during a later off-hours pass.
Downloaded folders are named:
<internal-id>__<sanitized-folder-name>.zip
The internal ID prevents collisions when NetSuite contains duplicate folder names.
The SQLite state database records:
- Internal ID and folder name
- Displayed size
- Page index and download URL
- Status and attempt count
- Final path and actual downloaded bytes
- SHA-256 hash
- Last error
If NetSuite redirects a download to a login page or returns HTML instead of a ZIP, the run stops initiating useful work and records the affected transfer as failed. Log in again and resume with:
python file_cabinet_downloader.py download --retry-failedThe volatile siaT, siaWhc, and siaNv values are intentionally excluded.
After authentication, the stable File Cabinet route is:
/app/common/media/mediaitemfolders.nl?sc=-63
The downloader automatically tries to import crawler.py and reuse:
crawler.is_logged_in(driver)
crawler.login(driver)If your function names differ, change only ensure_authenticated() in
file_cabinet_downloader.py; the scanning, checkpoint, scheduling, and download
logic can remain unchanged.
To expose this through the existing main.py, import the module and call its
main() through a new file-cabinet command. Keep the downloader in the same
project directory as crawler.py so the existing login module is importable.
- Run
scan. - Confirm the manifest reports 4,432 unique IDs.
- Confirm the displayed total size is close to 93 GB.
- Back up the SQLite state file.
- Run ten to twenty downloads with
--workers 1 --max-downloads 10. - Open several ZIPs manually.
- Resume with
--workers 2. - Check
python file_cabinet_downloader.py statusperiodically. - Do not delete
.partfiles while the process is running. - Preserve the SQLite database until the entire migration is verified.