# CRUD (Files)

Pods can store regular files (e.g., PDFs, photos, etc.) in addition to storing structured data as [Things](https://docs.inrupt.com/reference/glossary#thing) and [SolidDatasets](https://docs.inrupt.com/reference/glossary#soliddataset) (see [read-and-write-rdf-data](https://docs.inrupt.com/sdk/javascript-sdk/read-and-write-rdf-data "mention") for storing structured data).

The [**`solid-client`**](https://inrupt.github.io/solid-client-js/) library provides various [file handling functions](https://inrupt.github.io/solid-client-js/modules/resource_file.html). Like other data stored in a Pod, each [File](https://inrupt.github.io/solid-client-js/modules/interfaces.html#file) is a [Resource](https://docs.inrupt.com/reference/glossary#resource) with a distinct URL, which may or may not include the file extension.

### Required Access

The same access control mechanism applies to these files that applies to any other Resource in the Pod. As such, to perform file operations on restricted Resources (i.e., not open to the general public), the user must first authenticate as someone with the appropriate access. Then, to make authenticated requests, pass to the various read/write functions the authenticated Session’s **`fetch`** function. For more information on authentication, see [authentication](https://docs.inrupt.com/sdk/javascript-sdk/authentication "mention").

<table><thead><tr><th width="264.42578125">Action</th><th>Required Access</th></tr></thead><tbody><tr><td>Read a file</td><td><strong><code>Read</code></strong> access to the file.</td></tr><tr><td>To write a new file to a Container</td><td><p>Either <strong><code>Append</code></strong> or <strong><code>Write</code></strong> access to the Container, depending on the library function used:</p><p>To use <a href="https://inrupt.github.io/solid-client-js/modules/resource_file.html#savefileincontainer"><strong><code>saveFileInContainer()</code></strong></a>, the user must have <strong><code>Append</code></strong> and/or <strong><code>Write</code></strong> access to the Container.</p><p>To use <a href="https://inrupt.github.io/solid-client-js/modules/resource_file.html#overwritefile"><strong><code>overwriteFile()</code></strong></a>, the user must have both <strong><code>Write</code></strong> access to the Container and <strong><code>Write</code></strong> access to the target file.<br><br>To create access policies for yet to be created files, create a default member policy with <strong><code>Write</code></strong> access for the Container.</p></td></tr><tr><td>To replace an existing file in a Container</td><td><p>Either <strong><code>Append</code></strong> or <strong><code>Write</code></strong> access to the Container, depending on the library function used:</p><p>To use <a href="https://inrupt.github.io/solid-client-js/modules/resource_file.html#savefileincontainer"><strong><code>saveFileInContainer()</code></strong></a>, the user must have <strong><code>Append</code></strong> and/or <strong><code>Write</code></strong> access to the Container.</p><p>To use <a href="https://inrupt.github.io/solid-client-js/modules/resource_file.html#overwritefile"><strong><code>overwriteFile()</code></strong></a>, the user must have both <strong><code>Write</code></strong> access to the Container and <strong><code>Write</code></strong> access to the target File.</p></td></tr><tr><td>To delete an existing file in a Container</td><td>Both <strong><code>Write</code></strong> access to the File and <strong><code>Write</code></strong> access to the Container.</td></tr></tbody></table>

For files saved in a Pod, their URL acts as the unique identifier. Their URLs are relative to the Pod's URL. For example:

* **`https://storage.inrupt.com/{someIdentifier}/pictures/picture.jpg`**
* **`https://storage.inrupt.com/{someIdentifier}/data/inventory1.pdf`**

where **`https://storage.inrupt.com/{someIdentifier}/`** is the Pod's URL.

Inrupt’s **`solid-client`** library provides [**`getPodUrlAll`**](https://api.docs.inrupt.com/docs/developer-tools/api/javascript/solid-client/modules/profile_webid.html#getpodurlall) to get the Pod's URL or, if the user has multiple Pods, the list of Pod URLs.

```javascript
import { getPodUrlAll } from "@inrupt/solid-client";

// Returns a list of URLs

const mystorages = await getPodUrlAll(webID, { fetch: fetch });
```

### Read a File

To read a file, you can use [**`getFile()`**](https://inrupt.github.io/solid-client-js/modules/resource_file.html#getfile) to fetch the file content at the specified URL. The **`getFile()`** returns a File. Once fetched, you can decode appropriately.

{% hint style="info" %}
To use [**`getFile()`**](https://inrupt.github.io/solid-client-js/modules/resource_file.html#getfile), the user must have **`Read`** access for the file.
{% endhint %}

The following example uses **`getFile()`** to read the specified files. The example assumes the user has the appropriate access.

{% tabs %}
{% tab title="Browser" %}

<pre class="language-javascript"><code class="lang-javascript">// ... import statement for authentication, which includes the fetch function, is omitted for brevity.
<strong>import { getFile, isRawData, getContentType, getSourceUrl, } from "@inrupt/solid-client";
</strong>
// ... Various logic, including login logic, omitted for brevity.

// Read file from Pod 
async function readFileFromPod(fileURL) {
  try {
    // File (https://inrupt.github.io/solid-client-js/modules/interfaces.html#file) is a Blob (see https://developer.mozilla.org/docs/Web/API/Blob)
<strong>    const file = await getFile(
</strong><strong>      fileURL,               // File in Pod to Read
</strong><strong>      { fetch: fetch }       // fetch from authenticated session
</strong><strong>    );
</strong>
    console.log( **`Fetched a ${getContentType(file)} file from ${getSourceUrl(file)}.`);
    console.log(`The file is ${isRawData(file) ? "not " : ""}a dataset.`);

  } catch (err) {
    console.log(err);
  }
}
</code></pre>

{% endtab %}

{% tab title="Node.JS" %}

<pre class="language-javascript"><code class="lang-javascript">// ... import statement for authentication, which includes the fetch function, is omitted for brevity.

import { writeFile } from 'fs/promises';
<strong>import { getFile, isRawData, getContentType, getSourceUrl, } from "@inrupt/solid-client";
</strong>
const MY_POD_URL = "https://example.com/mypod/";

const session = new Session();

// ... Various logic, including login logic, omitted for brevity.

if (session.info.isLoggedIn) {
  readFileFromPod(`${MY_POD_URL}mypics/pigeon.jpg`, session.fetch, './downloaded-pigeon.jpg');
}

// ...

// Read file from Pod and save to local file
async function readFileFromPod(fileURL, fetch, saveAsFilename) {
  try {
<strong>    const file = await getFile(
</strong><strong>      fileURL,               // File in Pod to Read
</strong><strong>      { fetch: fetch }       // fetch from authenticated session
</strong><strong>    );
</strong>
    console.log(`Fetched a ${getContentType(file)} file from ${getSourceUrl(file)}.`);
    console.log(`The file is ${isRawData(file) ? "not " : ""}a dataset.`);

    const arrayBuffer = await file.arrayBuffer();
    writeFile(saveAsFilename, new Uint8Array(arrayBuffer));

  } catch (err) {
    console.log(err);
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

The above example uses:

* [`getFile()`](https://inrupt.github.io/solid-client-js/modules/resource_file.html#getfile) to fetch the File.
* [`getContentType()`](https://inrupt.github.io/solid-client-js/modules/resource_resource.html#getcontenttype) to return the content type. For example, **`text/plain;charset=UTF-8`** or **`image/svg+xml`** or **`text/html;charset=UTF-8`**. You can use `getContentType()` on any Resource, not just Files.
* [`isRawData()`](https://inrupt.github.io/solid-client-js/modules/resource_resource.html#israwdata) to determine if the Resource is raw data (i.e., not a SolidDataset). You can use `isRawData()` on any Resource, not just Files.

{% hint style="info" %}
You can use [`getFile()`](https://inrupt.github.io/solid-client-js/modules/resource_file.html#getfile) to retrieve a file that contains structured data. In this case, [`getContentType()`](https://inrupt.github.io/solid-client-js/modules/resource_resource.html#getcontenttype) on the returned File might return **`text/turtle; charset=UTF-8`** if the user’s Pod server defaults to returning structured data in that format. The [`isRawData()`](https://inrupt.github.io/solid-client-js/modules/resource_resource.html#israwdata) on the File returns **`false`**.
{% endhint %}

### Write a File

When writing a file to a Pod, you can:

* Use [`overwriteFile()`](https://inrupt.github.io/solid-client-js/modules/resource_file.html#overwritefile) to specify the exact destination file URL.
* Use [`saveFileInContainer()`](https://inrupt.github.io/solid-client-js/modules/resource_file.html#savefileincontainer) to specify only the URL for the parent [Container](https://docs.inrupt.com/reference/glossary#container).

#### Write a File to a Specific URL

To specify the file’s destination URL during the save, use `overwriteFile()`. To use `overwriteFile()`, pass it the following parameters:

<table data-header-hidden><thead><tr><th width="154"></th><th></th><th data-hidden></th></tr></thead><tbody><tr><td>File URL</td><td>The destination URL for the File. If a file already exists at that URL, the function <mark style="color:red;"><strong>overwrites</strong></mark> the existing file.</td><td></td></tr><tr><td>Options object</td><td><p>An object that includes the following options:</p><p><strong><code>{ contentType: &#x3C;MIME type>, fetch: &#x3C;fetch func> }</code></strong><br></p><p><strong>fetch</strong></p><p><strong><code>fetch</code></strong> function from an authenticated session if accessing restricted Resource (i.e., the general public cannot write file at the specified location). See <a data-mention href="authentication">authentication</a>.</p><p>Optional if the general public can write files at the specified location.</p><p><strong>contentType</strong></p><p>Optional. MIME type.</p></td><td></td></tr></tbody></table>

{% hint style="info" %}

* To use `overwriteFile()`, the user must have both **`Write`** access to the Container and **`Write`** access to the target file. Since the new file does not yet exist in the Container, the Container must have included **`Write`** access as its default member access.
* When using `overwriteFile()` to save the file to the destination URL, the Solid server creates any intermediate Container as needed.
  {% endhint %}

{% tabs %}
{% tab title="Browser" %}
The following example uses `overwriteFile()` to save the selected local files to the specified URL. The example assumes the user has the appropriate access.

<pre class="language-javascript"><code class="lang-javascript">// ... import statement for authentication, which includes the fetch function, is omitted for brevity.

<strong>import { overwriteFile, getSourceUrl } from "@inrupt/solid-client";
</strong>
// ... Various logic, including login logic, omitted for brevity.

const MY_POD_URL = "https://example.com/mypod/";

// Upload selected files to Pod
function handleFiles() {
  const fileList = document.getElementById('fileinput').files;

  fileList.forEach(file => {
    writeFileToPod(file, `${MY_POD_URL}uploadedFiles/${file.name}`, fetch);
  });
}

// Upload File to the targetFileURL.
// If the targetFileURL exists, overwrite the file.
// If the targetFileURL does not exist, create the file at the location.
async function writeFileToPod(file, targetFileURL, fetch ) {
  try {
<strong>    const savedFile = await overwriteFile(  
</strong><strong>      targetFileURL,                              // URL for the file.
</strong><strong>      file,                                       // File
</strong><strong>      { contentType: file.type, fetch: fetch }    // mimetype if known, fetch from the authenticated session
</strong><strong>    );
</strong>    console.log(`File saved at ${getSourceUrl(savedFile)}`);

  } catch (error) {
    console.error(error);
  }
}
</code></pre>

In the example, if the **`uploadedFiles`** Container does not exist when saving the file, the Solid server creates it to save the file to the specified URL.
{% endtab %}

{% tab title="Node.js" %}
The following example reads local files and uses `overwriteFile()` to save the files to the specified URL. The example assumes the user has the appropriate access.

<pre class="language-javascript"><code class="lang-javascript">// ... import statement for authentication, which includes the fetch function, is omitted for brevity.

import { readFile } from 'fs/promises';
<strong>import { overwriteFile, getSourceUrl } from "@inrupt/solid-client";
</strong>
const MY_POD_URL = "https://example.com/mypod/";

const session = new Session();

// ... Various logic, including login logic, omitted for brevity.
  
if (session.info.isLoggedIn) {
  uploadFile('./pigeon.jpg', "image/jpeg", `${MY_POD_URL}mypics/pigeon.jpg`, session.fetch);
  uploadFile('./report.pdf', "application/pdf",`${MY_POD_URL}mypdfs/report.pdf`, session.fetch);
}

// ...

// Read local file and save to targetURL
async function uploadFile(filepath, mimetype, targetURL, fetch) {
  try {
    const data = await readFile(filepath);
    writeFileToPod(data, mimetype, targetURL, fetch);
  } catch (err) {
    console.log(err);
  }
}

// Upload data as a file to the targetFileURL.
// If the targetFileURL exists, overwrite the file.
// If the targetFileURL does not exist, create the file at the location.
async function writeFileToPod(filedata, mimetype, targetFileURL, fetch) {
  try {
<strong>    const savedFile = await overwriteFile(  
</strong><strong>      targetFileURL,                   // URL for the file.
</strong><strong>      filedata,                        // Buffer containing file data
</strong><strong>      { contentType: mimetype, fetch: fetch } // mimetype if known, fetch from the authenticated session
</strong><strong>    );
</strong>    console.log(`File saved at ${getSourceUrl(savedFile)}`);
  } catch (error) {
    console.error(error);
  }
</code></pre>

In the example, if the `mypod` and `mypdfs` Containers do not exist when saving the file, the Solid server creates them to save the file to the specified URL.
{% endtab %}
{% endtabs %}

#### Write a File into an Existing Container

To specify only the URL of the parent Container during the save, i.e., to let the Solid server determine the name of your file in the Container, use [`saveFileInContainer()`](https://inrupt.github.io/solid-client-js/modules/resource_file.html#savefileincontainer). To use `saveFileInContainer()`, pass it the following parameters:

<table data-header-hidden><thead><tr><th width="154"></th><th></th><th data-hidden></th></tr></thead><tbody><tr><td>Container URL</td><td>The URL of the Container where you wish to place the file. The Container must already exist.</td><td></td></tr><tr><td>Options object</td><td><p>An object that includes the following options:</p><p><strong><code>{ slug: &#x3C;name>, contentType: &#x3C;MIME type>, fetch: &#x3C;fetch func> }</code></strong><br></p><p><strong>slug</strong></p><p>Optional. The suggested file name. There is no guarantee that the Solid server will use the <code>slug</code> as the saved file name.</p><p>If the Solid server decides to use the <code>slug</code> as the file name but the <code>slug</code> matches an already existing file in the specified Container, the Solid server creates a <strong>new</strong> name for your file. That is, the function does <strong>not</strong> overwrite existing files.</p><p><strong>fetch</strong></p><p><strong><code>fetch</code></strong> function from an authenticated session if accessing restricted Resource (i.e., the general public cannot write file at the specified location). See <a data-mention href="authentication">authentication</a>.</p><p>Optional if the general public can write files at the specified location.</p><p><strong>contentType</strong></p><p>Optional. MIME type.</p></td><td></td></tr></tbody></table>

{% hint style="info" %}

* To use `saveFileInContainer()`, the user must have **`Append`** and/or **`Write`** access to the Container. See [authentication](https://docs.inrupt.com/sdk/javascript-sdk/authentication "mention").
* With `saveFileInContainer()`, you do not control the name, and thus the URL, of your file. The Solid server may or may not use the suggested **`slug`** as the file name.
* If the specified Container does not exist, the save operation fails.
  {% endhint %}

The following example reads local files and uses `saveFileInContainer()` to save the files into the specified Container. The example assumes the user has the appropriate access.

{% tabs %}
{% tab title="Browser" %}

<pre class="language-javascript"><code class="lang-javascript">// ... import statement for authentication, which includes the fetch function, is omitted for brevity.

<strong>import { saveFileInContainer, getSourceUrl } from "@inrupt/solid-client";
</strong>
// ... Various logic, including login logic, omitted for brevity.

const MY_POD_URL = "https://example.com/mypod/";

// Upload selected files into Container
function handleFiles() {
  const fileList = document.getElementById('fileinput').files;

  fileList.forEach(file => {
    placeFileInContainer(file, `${MY_POD_URL}uploadedFiles/`);
  });
}

// Upload file into the targetContainer.
async function placeFileInContainer(file, targetContainerURL) {
  try {
<strong>    const savedFile = await saveFileInContainer(
</strong><strong>      targetContainerURL,           // Container URL
</strong><strong>      file,                         // File 
</strong><strong>      { slug: file.name, contentType: file.type, fetch: fetch }
</strong>    );
    console.log(`File saved at ${getSourceUrl(savedFile)}`);
  } catch (error) {
    console.error(error);
  }
}
</code></pre>

{% endtab %}

{% tab title="Node.js" %}

<pre class="language-javascript"><code class="lang-javascript">// ... import statement for authentication, which includes the fetch function, is omitted for brevity.

import { readFile } from 'fs/promises';
<strong>import { saveFileInContainer, getSourceUrl } from "@inrupt/solid-client";
</strong>
const MY_POD_URL = "https://example.com/mypod/";

const session = new Session();

// ... Various logic, including login logic, omitted for brevity.

if (session.info.isLoggedIn) {
  readAndSaveFile('./pigeon.jpg', "image/jpeg", `${MY_POD_URL}mypics/`, "mypigeon.jpg", session.fetch);
  readAndSaveFile('./report.pdf', "application/pdf", `${MY_POD_URL}mypdfs/`, "myreport.pdf", session.fetch);
}

// ...

// Read local file and upload into a Container
async function readAndSaveFile(filepath, mimetype, containerURL, slug, fetch) {
  
  try {
    const data = await readFile(filepath);
    placeFileInContainer(data, mimetype, containerURL, slug, fetch);
  } catch (err) {
    console.log(err);
  }
}

// Upload data as a file into the targetContainer.
async function placeFileInContainer(filedata, mimetype, targetContainerURL, slug, fetch) {
  try {
<strong>    const savedFile = await saveFileInContainer(
</strong><strong>      targetContainerURL,           // Container URL
</strong><strong>      filedata,                     // Buffer containing file data
</strong><strong>      { slug: slug, contentType: mimetype, fetch: fetch }
</strong><strong>    );
</strong>    console.log(`File saved at ${getSourceUrl(savedFile)}`);
  } catch (error) {
    console.error(error);
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

After saving the file, the example uses [`getSourceUrl`](https://inrupt.github.io/solid-client-js/modules/resource_resource.html#getsourceurl) on the returned file to determine the saved filename.

### Delete a File

To delete a file, you can use [`deleteFile()`](https://inrupt.github.io/solid-client-js/modules/resource_file.html#deletefile) to remove the file at the specified URL. To use `deleteFile()`, pass it the following parameters:

<table data-header-hidden><thead><tr><th width="227.5"></th><th></th></tr></thead><tbody><tr><td>File URL</td><td>The URL of the file to delete.</td></tr><tr><td>Options object</td><td><p>An object that includes the following option:<br><strong><code>{ fetch: &#x3C;fetch func> }</code></strong><br><br><strong>fetch</strong><br><code>fetch</code> function from an authenticated session if deleting a restricted Resource (i.e., the general public cannot delete the specified File). See <a data-mention href="authentication">authentication</a>.</p><p>Optional if the general public can delete the Resource.<br></p></td></tr></tbody></table>

{% hint style="info" %}
The user must have **`Write`** access to the File.
{% endhint %}

The following example uses `deleteFile()` to delete the specified file. The example assumes the user has the appropriate access.

{% tabs %}
{% tab title="Browser" %}

<pre class="language-javascript"><code class="lang-javascript">// ... import statement for authentication, which includes the fetch function, is omitted for brevity.

<strong>import { deleteFile } from "@inrupt/solid-client";
</strong>
// ... Various logic, including login logic, omitted for brevity.

try {
  // Delete the specified file from the Pod.
<strong>  await deleteFile(
</strong><strong>    "https://example.com/some/boring/file",  // File to delete
</strong><strong>    { fetch: fetch }                         // fetch function from authenticated session
</strong>  );
  console.log("Deleted::  https://example.com/some/boring/file");
} catch (err) {
  console.error(err);
}
</code></pre>

{% endtab %}

{% tab title="Node.js" %}

```javascript
// ... import statement for authentication, which includes the fetch function, is omitted for brevity.

import { deleteFile } from "@inrupt/solid-client";

const session = new Session();

// ... Various logic, including login logic, omitted for brevity.

if (session.info.isLoggedIn) {
  deleteFileFromPod("https://example.com/mypod/mypics/pigeon.jpg", session.fetch);
}

// ...


async function deleteFileFromPod(fileURL, fetch) {
  try {
    await deleteFile(
      fileURL,          // File to delete from Pod
      { fetch: fetch }   // fetch from the authenticated session
    );
    console.log(`File deleted at ${fileURL}`);
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}
{% endtabs %}
