File Upload Fields in WordPress Forms: Logging Metadata to Google Sheets
You can't embed a PDF in a spreadsheet cell. Here's the pattern teams use to log file uploads to Google Sheets in a way that actually helps the people who need them.
In This Guide
- The Problem With File Uploads in Spreadsheets
- The Six Columns Every Upload Row Should Have
- Public vs. Private Files
- Gravity Forms File Uploads
- WPForms File Uploads
- Fluent Forms and Formidable
- Virus Scanning Before Logging
- Image Previews Inside Sheets
- Storage Planning and Retention
- Beyond Logging: Automating What Comes Next
- Recap
- Frequently Asked Questions
The Problem With File Uploads in Spreadsheets
Google Sheets can't hold a file. A cell stores text, numbers, dates, formulas - but not a 5MB PDF or a 200KB image. So when a WordPress form has a file upload field and the team asks to "see uploads in the sheet," there's a translation problem.
The right answer is metadata. Your sheet shouldn't contain the file - it should contain everything you need to find, audit, and manage the file. That's a hyperlink to the file, the filename, the size, the MIME type, and ideally a few audit columns: who uploaded, when, and whether the file passed a virus scan.
Teams that get this right end up with a sheet that's a working operational tool. Teams that don't end up with a sheet full of broken links, cryptic IDs, or - worst - no useful trace of the upload at all.
The Six Columns Every Upload Row Should Have
Every file upload submission should populate at least six columns in your sheet.
File URL - the direct link to the file. Use HYPERLINK() so the cell is clickable.
Filename - the original name the user uploaded. Helps with searching ("find that contract from Acme Corp") and gives context the URL doesn't.
Size (bytes) - useful for spotting suspicious uploads, audit reporting, and storage planning. Format the column as a number with thousand separators.
MIME type - tells you what the file actually is, not just what its extension claims. A file named `contract.pdf` with MIME type `application/x-msdownload` is a red flag.
Uploaded At - timestamp of upload. Important when files might be replaced or revisited.
Form / Field - which form and which field this file came from. Critical when you have multiple upload fields per form, or many forms feeding the same sheet.
Public vs. Private Files
WordPress form plugins handle file privacy differently. Some store uploads in `/wp-content/uploads/` where anyone with the URL can fetch them. Others store them outside the web root and proxy access through a plugin endpoint that checks permissions.
This matters for your sheet. If your sheet is shared (with sales, with finance, with a vendor), and your file URLs are public, anyone who sees the sheet can fetch the files. For a contact form that uploads a portfolio sample, that's fine. For a job application that uploads a passport scan, that's a leak.
The practical pattern: configure your form plugin to keep sensitive uploads private (proxied URLs that require WordPress login). Add a column to your sheet labeled "Access" with values like Public or Private. Train your team to expect a login prompt when they click a Private link.
Gravity Forms File Uploads
Gravity Forms stores uploads in `/wp-content/uploads/gravity_forms/` by default. The submission entry holds an array of file URLs (one per upload, since fields can be multi-file). When you map this field to Sheets, decide between flatten (all URLs in one cell separated by newlines) or expand (one row per file).
For a portfolio form where each application has 3-5 work samples, expand is easier to scan. For a contact form that occasionally has a single attachment, flatten works fine.
SheetLink Forms' Gravity Forms integration supports both, configurable per field. It also enriches each row with size, MIME type, and SHA-256 hash for integrity checks if you need them.
WPForms File Uploads
WPForms (Pro tier required for the file upload field) stores uploads in a private directory by default and serves them through a signed URL endpoint. This is more secure than Gravity Forms' default but means the URL in your sheet expires.
Two strategies. Long-expiring URLs (months to years) are simple but mean a leaked sheet means leaked files. Short-expiring URLs (24 hours) are safer but require regenerating before each access.
The practical answer for most teams: log the WordPress entry ID and field ID, plus a "Click to view" link that resolves through the plugin and respects current permissions. The sheet stays useful, the files stay protected.
We go deeper on this pattern in our WPForms-to-Sheets guide.
Fluent Forms and Formidable
Fluent Forms and Formidable Forms both expose file URLs cleanly through their submission hooks. Mapping is straightforward - the field exports a single URL or array of URLs, and the plugin extracts metadata from each.
The edge case: both plugins allow file uploads inside repeater fields. When you have a repeater containing a file field (e.g., "list each property and attach a photo"), the integration needs to handle nested arrays. SheetLink Forms' integrations for both Fluent Forms and Formidable handle this in either flatten or expand mode.
Virus Scanning Before Logging
If your forms accept files from the public internet, you should scan them. Most WordPress hosts run ClamAV or similar at the server level, but it's often set to silent quarantine - infected files get moved aside without notifying anyone.
A better pattern: scan at upload time, log the result to your sheet, and reject infected files outright. SheetLink Forms can call a ClamAV endpoint or VirusTotal API on every upload, write the verdict to a "Scan" column, and refuse to write the row at all if a known threat is detected.
For compliance-heavy use cases (HIPAA, financial intake, government work), this turns the sheet into an audit log: every file logged means it passed a scan, and every quarantined upload is recorded separately. GDPR-compliant form workflows typically require this level of traceability.
Image Previews Inside Sheets
For image uploads specifically, Sheets has the IMAGE() formula. You can put `=IMAGE(B2)` next to the URL column and Sheets renders a thumbnail in the cell. This works for public URLs only - private/signed URLs won't load because Sheets fetches the image as an anonymous client.
For most teams, IMAGE() previews are nice to have but not essential. They slow down the sheet on large datasets (>1,000 rows). If you only need previews for recent submissions, use a filtered view or a separate "Recent Uploads" tab with the IMAGE() formula applied only to the last 50 rows.
Storage Planning and Retention
A form site that accepts files for a year quietly accumulates gigabytes. Your sheet helps you track this if you log size in bytes and add a SUMIF over a date range.
A simple "Storage Used Last 30 Days" formula: `=SUMIFS(C:C, D:D, ">="&TODAY()-30) / 1024 / 1024` returns megabytes. Combined with retention rules (delete files older than 90 days unless flagged "keep"), this turns Sheets into your storage dashboard.
For agencies and teams handling many client sites, this kind of visibility is non-negotiable. You don't want to find out about a 50GB upload directory by hitting a host quota.
Beyond Logging: Automating What Comes Next
Once file metadata is in Sheets, you can drive workflows from it. A new upload triggers an email to the assigned reviewer (a Sheets-side script). A specific MIME type routes to a specialist queue (the Multi-Node Routing add-on). A flagged virus scan pings Slack (the CRM Fan-Out add-on).
None of this depends on the file being inside the sheet. The metadata in your six columns is enough.
Recap
You can't put files in a spreadsheet. You can put metadata that's good enough for almost every operational use case: a clickable URL, a filename, a size, a MIME type, a timestamp, and a form/field reference.
When privacy matters, use proxied URLs that respect login state. When integrity matters, log a hash. When compliance matters, log a virus scan verdict. The sheet becomes a working tool, not a graveyard of broken links.
Frequently Asked Questions
Why can't I embed the file in a Google Sheets cell?
A spreadsheet cell stores text, numbers, dates, or formulas. Files are not a supported cell type. The standard pattern is to log a clickable URL plus metadata like filename, size, and MIME type.
What if my form uploads contain sensitive data?
Use private/signed URLs that require login to access, not public WordPress upload URLs. Add an "Access" column to your sheet to flag rows with private files. Keep the sheet sharing scope tight (specific people, not "anyone with the link").
Should I use IMAGE() to preview images in the sheet?
Only if your file URLs are public. IMAGE() fetches as an anonymous client and won't load private/signed URLs. For sensitive uploads, skip the preview.
How do I log a virus scan result?
Add a "Scan" column to your sheet. SheetLink Forms can call ClamAV or VirusTotal on every upload, write the verdict to that column, and reject the row if a known threat is detected.
How do I handle multiple files in one upload field?
Two strategies. Flatten: combine all file URLs in one cell with separators. Expand: each file becomes its own row with the parent submission fields duplicated. Choose based on whether you need to filter individual files or just see the submission as a whole.
Does this work with Gravity Forms, WPForms, Fluent, and Formidable?
Yes. SheetLink Forms supports file upload metadata logging for every major form builder. Each integration extracts the URL, filename, size, and MIME type from the form's native field structure.
How do I plan for storage growth?
Log file size in bytes for every upload. Add a SUMIF formula over a date range to see storage growth. Apply a retention policy (e.g., delete files older than 90 days unless flagged "keep") and run it via a scheduled cron or Apps Script trigger.
Can I store the file hash for integrity checks?
Yes. SheetLink Forms can compute a SHA-256 hash on upload and write it to a dedicated column. Useful for compliance audits and detecting tampering.
Log Every File Upload to Sheets
URLs, metadata, virus scan results, and audit columns. SheetLink Forms handles it all out of the box.