> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/open-save-document/save.md).

# Save a Document with JavaScript

Learn how to save a document with JavaScript. Explore methods like triggering downloads, saving locally, or storing on servers. Discover WebViewer APIs for seamless implementation. The Apryse Web SDK

There are numerous methods of saving a document depending on what you need to do. This may involve triggering a download on the client to save to the local file system or saving the file data to the server. Regardless of the outcome, WebViewer has APIs to support the features you would like to implement.

## Getting file data

The standard way to retrieve document data is by getting the [Document](https://sdk.apryse.com/api/web/Core.Document.html) object and using the [`getFileData`](https://sdk.apryse.com/api/web/Core.Document.html#getFileData__anchor) API.

{% hint style="info" %}
Calling `exportAnnotations` will serialize all the annotations which can impact performance if there are tens of thousands of annotations in a document. There is [an alternative way to get file data](#getting-file-data-alternative) that you can use if you have large amounts of annotations.
{% endhint %}

Below is a basic example of setting up WebViewer and using the `getFileData` API to retrieve document data by clicking a custom header button.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">WebViewer({
  initialDoc: "mydoc.pdf",
  licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
}, document.getElementById('viewer'))
  .then(instance => {
    const { documentViewer, annotationManager } = instance.Core;

    // Add header button that will get file data on click
    instance.UI.setHeaderItems(header => {
      header.push({
          type: 'actionButton',
          img: '...',
          onClick: async () => {
            const doc = documentViewer.getDocument();
            const xfdfString = await annotationManager.exportAnnotations();
            const data = await doc.getFileData({
              // saves the document with annotations in it
              xfdfString
            });
            const arr = new Uint8Array(data);
            const blob = new Blob([arr], { type: 'application/pdf' });

            // Add code for handling Blob here
          }
      });
    });
  });
</code></pre>

[DocumentViewer#getDocument](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#getDocument) [Document#getFileData](https://sdk.apryse.com/api/web/Core.Document.html#getFileData)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">WebViewer({
  initialDoc: "mydoc.pdf",
  licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
}, document.getElementById('viewer'))
  .then(instance => {
    const { docViewer, annotManager } = instance;

    // Add header button that will get file data on click
    instance.setHeaderItems(header => {
      header.push({
          type: 'actionButton',
          img: '...',
          onClick: async () => {
            const doc = docViewer.getDocument();
            const xfdfString = await annotManager.exportAnnotations();
            const data = await doc.getFileData({
              // saves the document with annotations in it
              xfdfString
            });
            const arr = new Uint8Array(data);
            const blob = new Blob([arr], { type: 'application/pdf' });

            // Add code for handling Blob here
          }
      });
    });
  });
</code></pre>

[DocumentViewer#getDocument](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#getDocument) [Document#getFileData](https://sdk.apryse.com/api/web/Core.Document.html#getFileData)
{% endtab %}
{% endtabs %}

The [`getFileData`](https://sdk.apryse.com/api/web/Core.Document.html#getFileData__anchor) API also takes in an options object to change some of the characteristics of the saved file data. The most commonly used options are:

* `xfdfString`: A string containing the [XFDF](/web/annotation/xfdf.md) annotation data to include in the downloaded file. This can be retrieved from the [`exportAnnotations`](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#exportAnnotations__anchor) function on [`AnnotationManager`](https://sdk.apryse.com/api/web/Core.AnnotationManager.html).
* `downloadType`: A string value for the output format. Possible values are:
  * `'pdf'`: Get file data in a PDF format. This is the default option and can be used for any loaded document.
  * `'office'`: If an office file is loaded (docx, xlsx, pptx, etc) then get file data in the current document format.
* `flags`: optional enum flags for how to save the PDF data. They can be found in [`Core.SaveOptions`](https://sdk.apryse.com/api/web/Core.html#.SaveOptions__anchor) and the most commonly used values are:
  * `REMOVE_UNUSED`: Remove unused PDF data during save. This is the default option.
  * `LINEARIZED`: Optimize data for speed and remove unused data.

### Saving Many Annotations Efficiently

Using the [getFileData](https://sdk.apryse.com/api/web/Core.Document.html#getFileData__anchor) API along with [exportAnnotations](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#exportAnnotations) as displayed in the section above is the standard way to get file data, but isn’t the most efficient. This is because calling `exportAnnotations` will serialize all the annotations, even though they haven’t been touched. Although this is more complete, it may not be necessary and can impact performance if there are tens of thousands of annotations in a document. If the document already has these annotations and they are unchanged, it is more efficient to only export the annotations that are new or changed.

Use the [exportDocumentAnnotationCommand](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#exportDocumentAnnotationCommand) for an XFDF command that represents the updates made to annotations since you last opened the document or since you last merged XFDF. You can pass this XFDF to `getFileData` using the `xfdfCommand` option. Please note that the `xfdfString` option will take priority if both are used.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```javascript
const xfdfCommand = await annotationManager.exportDocumentAnnotationCommand()
const data = await doc.getFileData({
    // updates the document with only annotations that changed
    xfdfCommand
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

[AnnotationManager#exportDocumentAnnotationCommand](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#exportDocumentAnnotationCommand)

## Using the document data

After getting a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob/) object, there are a variety of ways to save it from the browser:

{% tabs %}
{% tab title="Browser API" %}
You can use the APIs found in the browser to open the document and allow downloading it.

{% code lineNumbers="true" %}

```js
const url = URL.createObjectURL(blob);
window.open(url);
```

{% endcode %}
{% endtab %}

{% tab title="FileSaver API" %}
You can also use a library like [FileSaver.js](https://github.com/eligrey/FileSaver.js/) to help facilitate a direct download.

{% code lineNumbers="true" %}

```js
// using the FileSaver.js library saveAs function, but any technique to save a Blob from the browsers works
window.saveAs(blob, 'downloaded.pdf');
```

{% endcode %}

[saveAs](https://github.com/eligrey/FileSaver.js/)
{% endtab %}

{% tab title="Fetch API" %}
You can save the binary data of the document to your remote server using the browser fetch API. This is not recommended for larger files. Saving the [annotation XFDF](/web/annotation/import-export.md) string is recommended.

{% code lineNumbers="true" %}

```js
const data = new FormData();
data.append('mydoc.pdf', blob, 'mydoc.pdf');
// depending on the server, 'FormData' might not be required and can just send the Blob directly

const res = await fetch('/api/test', {
  method: 'POST',
  body: data,
});
```

{% endcode %}
{% endtab %}

{% tab title="XMLHttpRequest API" %}
You can save the binary data of the document to your remote server using the browser [XMLHttpRequest API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/). This is not recommended for larger files. Saving the [annotation XFDF](/web/annotation/import-export.md) string is recommended.

{% code lineNumbers="true" %}

```js
const data = new FormData();
data.append('mydoc.pdf', blob, 'mydoc.pdf');
// depending on the server, 'FormData' might not be required and can just send the Blob directly

const req = new XMLHttpRequest();
req.open("POST", '/api/test', true);
req.onload = function(oEvent) {
  // Uploaded.
};

req.send(data);
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Downloading with the UI

The [`UI`](/web/what-is-webviewer/usage.md#ui-namespace) namespace provides a [`downloadPdf`](https://sdk.apryse.com/api/web/UI.html#.downloadPdf) API with similar options to [`getFileData`](https://sdk.apryse.com/api/web/Core.Document.html#getFileData).

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
const options = {
  filename: 'myDocument.pdf',
  xfdfString,
  flags: Core.SaveOptions.LINEARIZED,
  downloadType: 'pdf'
};

instance.UI.downloadPdf(options);
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
const options = {
  filename: 'myDocument.pdf',
  xfdfString,
  flags: CoreControls.SaveOptions.LINEARIZED,
  downloadType: 'pdf'
};

instance.downloadPdf(options);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Get document data without the viewer

Getting document data [without a viewer](/web/get-started/without-viewer.md) is possible and very similar to the method mentioned in this guide.

[Get document data without a viewer](/web/get-started/guides/get-file-data-without-viewer.md) To get document data as a blob without a viewer.

## Learn more

[Loading and saving annotations](/web/annotation/import-export.md) To import/load and export/save annotations with a PDF document.

[Saving a document in Salesforce](/salesforce/annotations/saving-documents-salesforce.md) To save a PDF through WebViewer in a Salesforce deployment.

## Additional external resources

[Sending data with XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data/#Sending_binary_data) Mozilla documentation on sending binary data using XMLHttpRequest.

[Sending data with fetch](https://developers.google.com/web/ilt/pwa/working-with-the-fetch-api/#example_post_requests) Google documentation on working with fetch API.

[Sending post request with jQuery](https://api.jquery.com/jquery.post/) jQuery documentation on using post requests.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/open-save-document/save.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
