File attachments!

To highlight one line from the docs:

When you publish (or share) a notebook, only the files currently used by the notebook will become public.

So, if you use a bunch of files during development, but you’re only using one file when you publish, then only the one file will be made public. The others will remain private. They’re still be visible to you (if you need them), but only files that are actually used by your notebook will be made public on publish. You can verify this by visiting your notebook in incognito mode, and we also show a little lock icon :lock: next to private files.

If you replace a file on an already-published notebook and then republish, both files will remain public. But since notebooks can also be imported as components, that’s generally a good thing because it keeps imports of an earlier version working.

It’s on our backlog to make working with file attachments easier. For example, I very much want file attachments to be incorporated into notebook history so that you can replace a file without having to rename it, and everything just works, and you don’t need to accumulate lots of files. I don’t know when we’ll be able to work on this, but we’ll keep you posted when we do.

4 Likes

thanks - that’s really helpful to know

Is there a way to programmatically list all file attachments in a notebook? I wanted an array with a list of the file names.

I don’t think there’s a way to do that programmatically, but maybe a zip file attachment could fit your usecase? Working with .zip files in Observable notebooks / Observable / Observable

with some patience you could hardcode fileattachments individually to make a list, but I don’t recommend it unless you’re in a bind

stateAttachments = new Map([
  ["Alabama", FileAttachment("Alabama.png")],
  ["Alaska", FileAttachment("Alaska.png")],
  ["Arizona", FileAttachment("Arizona.png")],
  ["Arkansas", FileAttachment("Arkansas.png")],
  ["California", FileAttachment("California.png")],
  ["Colorado", FileAttachment("Colorado.png")],
  ["Connecticut", FileAttachment("Connecticut.png")],
  /* ... */
])

Thanks for the suggestions @asg017, but ideally I would need a list of the file names…

My use case: an interface where the user can upload some data, select a few parameters (including selecting the file for the data they just uploaded), and output a chart.

Unofficially you can fetch all attachment names via the console:

  1. Open your notebook in Next. (If you already have it open but have since added new attachments, reload the notebook.)
  2. Open the developer tools console.
  3. Type:
    __NEXT_DATA__.props.pageProps.initialNotebook.files.map(d => d.name)
    

Are you referring to notebook authors or notebook visitors? Visitor uploads only live in the browser and are never sent to the server (unless you integrate some third party services).

FileAttachments can only be added by the notebook author, and there is currently no official way to retrieve them dynamically.

However, If the notebook is at least linkshared, you can extract them from the notebook’s module file. Example:

files = {
  const url = 'https://api.observablehq.com/@observablehq/file-attachments.js?v=3';
  const src = await (await fetch(url)).text();
  const map = (src.match(/\n\s+const fileAttachments = (.+);\n/) || [])[1];
  return map ? eval(map) : null;
}

Edit: Here is a demo:

1 Like

I’m talking about notebook authors.

Explaining the use case in more detail: I want the notebook (let’s call it a “Chart Generator”) to be as simple as possible for users without coding experience. Ideally:

  1. They fork the “Chart Generator” template notebook
  2. They attach some csv data
  3. A select box allows them to pick that file they have just attached
  4. With a few more inputs, they select some configs (type: bar chart, color: red, etc.)
  5. The notebook plots a chart

The missing part for me here is step 3, and that’s why I would need an array with the list of file attachments. It would be exactly the output of __NEXT_DATA__.props.pageProps.initialNotebook.files.map(d => d.name) which you pointed out, but accessible within the notebook.

1 Like

Would considering using local files rather than file attachments be an option?

This has the advantage that there is no uploading. The select box would become the file dialog.

This is interesting! I guess the only issue is that files won’t be persisted. But thanks for pointing this out, I’m going to consider it!

If necessary, it may be possible to use local storage to persist files (as blobs or object urls), or file names:

or perhaps something less abstract.

There is also

which allows for a file attachment as a default value for a local file.

1 Like