Skip to content
Scava Docs Join the waitlist

Scava opens soon. These docs describe how it works at launch. Join the waitlist

All chapters

Data

File uploads for an internal tool - scava.files

An app on Scava can store files that people upload: photos of receipts, PDFs, spreadsheets, scans. The app calls scava.files('name'). The files are stored by Scava, protected by the same sign-in and the same kind of rules as entries, and they count against the storage of the owner's plan. There is no bucket to set up.

The calls #

const receipts = scava.files('receipts');   // a collection of files; name rules like scava.db

const file = await receipts.upload(inputElement.files[0]);
// { id, name, type, size, created_by, created_at, url }

const files = await receipts.list();         // newest first
const one = await receipts.get(file.id);
const address = receipts.url(file.id);       // for <img src> or <a href>
await receipts.remove(file.id);

A complete example #

<input type="file" id="pick" accept="image/*">
<div id="gallery"></div>
<script>
  const photos = scava.files('photos');

  async function show() {
    const gallery = document.getElementById('gallery');
    gallery.textContent = '';
    for (const f of await photos.list()) {
      const img = document.createElement('img');
      img.src = photos.url(f.id);
      img.alt = f.name;
      img.width = 160;
      gallery.append(img);
    }
  }

  document.getElementById('pick').addEventListener('change', async (event) => {
    await photos.upload(event.target.files[0]);
    show();
  });

  show();
</script>

Rules #

Files follow rules under files in scava.json, with the same values as for entries.

{
  "files": {
    "receipts": { "read": "own", "write": "own" },
    "handbook": { "read": "all", "write": ["owner", "group:hr"] }
  }
}

The rules are enforced on Scava's server on every request. See Permission rules.

What opens in the browser and what downloads #

A file you can open on the app's address could be an attack if it were allowed to run. So Scava shows only harmless types and downloads the rest.

Opens in the browserAlways downloads
Images: PNG, JPEG, GIF, WebP, AVIF, BMP, ICOHTML and SVG
PDF, in the browser's own viewerScripts and any unknown type
Audio: MP3, WAV, OGG, M4AOffice files such as DOCX, XLSX, PPTX
Video: MP4, WebM, MOVJSON, XML, ZIP and everything else
Plain text and CSV

Pictures, sound, video and text are served inside a sandbox that cannot run anything. Files that download are sent as binary attachments with the content type fixed, so an uploaded file can never become part of the app.

What is refused #

Limits #

WhatLimit
Size of one file25 MB
Files per app100,000
Bytes per app50 GB
Changes per person and app300 in 10 minutes, uploads and deletions count like entries

Files count against the storage of the owner's plan together with entries and the files of the app itself. If the storage is full, uploads fail with "This app has reached its storage limit."

In server code #

Server code uses env.SCAVA.files('receipts') with list, get, put(name, data, { type }), read(id) (which returns the details and the bytes in .data), remove and url. See Server code.

Export and deletion #

The owner's download of all data of an app includes all uploaded files. Deleting the app deletes its files.