# How to download a file by URL and save as attachment?

Sometimes you need to download a file from an external URL and attach it to your record. This could be useful for automatically downloading images, documents, or other files referenced by URLs stored in your app.

This guide shows you how to create an action that downloads a file from a URL and saves it as an attachment to your record.

## Overview[​](#overview "Direct link to Overview")

The process involves:

1. getting the file URL (from a field or using a default)
2. using the RestApi to download the file
3. persisting the downloaded file
4. adding it to the record's attachments list

## Implementation[​](#implementation "Direct link to Implementation")

In your `actions/logic/index.ts` file, add the following action function and export it. You can either create a new action called `test_download_image` or add this code to an existing action like `add` or `edit`:

```
import { entity } from "#typings";
import { Files, RestApi } from "@comind/api/server";

function test_download_image() {
  // Default file URL if none is provided
  const defaultImageToDownload = "https://example.com/sample-logo.png";

  // Get the URL from a field (c_file_url) or use default
  const fileUrl = entity.c_file_url || defaultImageToDownload;

  // Extract filename from URL, fallback to default name
  const fileName = fileUrl.split("/").pop() || "downloaded_file.png";

  // Create REST client and download the file
  RestApi.createClient(fileUrl);
  RestApi.createRequest("", "GET");
  // Optional:
  // RestApi.addHeader('Authorization', 'Bearer ' + SOME_AUTH_TOKEN);
  const res = RestApi.downloadFile();

  // Check if download was successful
  const isOk =
    res?.statusCode === 200 && res?.downloadedFileRef && res?.contentLength > 0;

  if (!isOk) {
    throw `Failed to download file: ${fileUrl} / ${JSON.stringify(res)}`;
  }

  // Persist the downloaded file
  const storedFile = Files.persistFileRef(res.downloadedFileRef.ref);

  // Initialize attachments list if it doesn't exist
  entity.attachments__list ??= [];

  // Add the file to attachments
  entity.attachments__list.push({
    file_uid: storedFile.uploadedFileId,
    title: fileName,
  });
}

export default {
  test_download_image,
  // ... other actions
} satisfies ActionLogic<typeof entity>;
```

## How it works[​](#how-it-works "Direct link to How it works")

1. **URL source**: the action gets the file URL from the `c_file_url` field. If this field is empty, it uses a default URL.

2. **File download**: the `RestApi.downloadFile()` method downloads the file and returns a response object containing:

   * `statusCode` - HTTP status code
   * `downloadedFileRef` - reference to the downloaded file
   * `contentLength` - size of the downloaded file

3. **Error handling**: the action checks if the download was successful by verifying the status code is 200 and that file reference and content length are present.

4. **File persistence**: the `Files.persistFileRef()` method saves the downloaded file to the system and returns an object with `uploadedFileId`.

5. **Attachment**: the file is added to the record's attachments list with the extracted filename as the title.

## Authorization[​](#authorization "Direct link to Authorization")

If the file URL requires authentication, you can add authorization headers before downloading:

```
RestApi.addHeader("Authorization", "Bearer " + SOME_AUTH_TOKEN);
```

This is useful when downloading files from:

* protected APIs that require API keys
* services that need bearer tokens
* endpoints requiring custom authentication headers

## Field requirements[​](#field-requirements "Direct link to Field requirements")

For this action to work, ensure your app has:

* a text field named `c_file_url` (or modify the field name in the code)
* an attachments list field named `attachments__list`

## Error handling[​](#error-handling "Direct link to Error handling")

The action includes basic error handling:

* throws an error if the download fails
* provides details about the failure including the URL and response

## Customization options[​](#customization-options "Direct link to Customization options")

You can customize this action by:

* **Changing the URL source**: modify `entity.c_file_url` to use a different field
* **Custom filename**: instead of extracting from URL, you could use a separate field for the filename
* **File validation**: add checks for file type, size, or other criteria before downloading
* **Multiple URLs**: extend to download multiple files from a list of URLs

## Example usage[​](#example-usage "Direct link to Example usage")

1. enter a file URL in the `c_file_url` field (e.g., `https://docs.comind.work/img/logo.png` or `https://example.com/document.pdf`)
2. click the "Test Download Image" action button (or trigger the action from your existing action)
3. the file will be downloaded and appear in the attachments list

This approach is particularly useful for:

* importing files from external systems
* automatically downloading referenced documents
* creating backups of external files
* bulk file imports from URL lists

## Additional processing[​](#additional-processing "Direct link to Additional processing")

Note that after downloading the file, you can also parse its content for further processing. This is especially useful for:

* **Excel files**: parse spreadsheet data and import rows into your app
* **CSV files**: process tabular data and create records automatically
* **Text files**: extract and analyze text content
* **JSON/XML files**: parse structured data and update related fields
* **Images**: extract metadata or perform image analysis

You can access the downloaded file content before persisting it, allowing you to read and process the file data as needed for your specific use case.

## Related[​](#related "Direct link to Related")

* [Actions](/developer-guide/building-blocks/actions.md) - action definitions, logic, and preconditions reference
* [External API calls](/developer-guide/how-to/advanced/external-api-calls.md) - the full `RestApi` builder pattern including authentication and error handling

## Video demonstration[​](#video-demonstration "Direct link to Video demonstration")

Your browser does not support the video tag.
