Scanning Documents to Web Pages with JavaScript and Dynamic Web TWAIN RESTful API
Tags: webdev, javascript, programming, restapi
Published: 2024-06-18 05:13:39

**Dynamic Web TWAIN** offers two methods for scanning documents from a web page: the **HTML5/JavaScript API** and the **RESTful API**. The former is suitable for web applications that require a high level of customization, while the latter is ideal for enterprise-class web applications, such as **Salesforce**, **Oracle APEX**, and **Microsoft Power Apps**, that need to scan documents in a sandboxed or security-restricted environment. This article will focus on the RESTful API and demonstrate how to scan paper documents to web pages using JavaScript and HTTP requests.


## Online Demo
[https://yushulx.me/web-twain-document-scan-management/examples/rest_api/](https://yushulx.me/web-twain-document-scan-management/examples/rest_api/)

## Prerequisites
-  Install and run Dynamsoft Service.
    - Windows: [Dynamsoft-Service-Setup.msi](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.msi)
    - macOS: [Dynamsoft-Service-Setup.pkg](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.pkg)
    - Linux: 
        - [Dynamsoft-Service-Setup.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.deb)
        - [Dynamsoft-Service-Setup-arm64.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup-arm64.deb)
        - [Dynamsoft-Service-Setup-mips64el.deb](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup-mips64el.deb)
        - [Dynamsoft-Service-Setup.rpm](https://demo.dynamsoft.com/DWT/DWTResources/dist/DynamsoftServiceSetup.rpm)
    
- Obtain a [free trial license](https://www.dynamsoft.com/customer/license/trialLicense?product=dwt&source=codepool) for Dynamic Web TWAIN.
- Include [axios](https://www.npmjs.com/package/axios).

    Axios is a promise-based HTTP client for the browser and Node.js. Since we have used it in a Node.js example, migrating the RESTful API calls to a web page is straightforward.

    ```html
    <script src="https://unpkg.com/axios@1.6.7/dist/axios.min.js"></script>
    ```
    
## Step 1: Accessing Dynamic Web TWAIN REST API via HTTP and HTTPS
The Dynamsoft Service runs on default ports `18622` for HTTP and `18623` for HTTPS..

To construct the host URL for the RESTful API:

```javascript
const port = window.location.protocol === 'https:' ? 18623 : 18622;
const host = window.location.protocol + '//' + "127.0.0.1:" + port;
```

## Step 2: Enumerating Scanners
The endpoint `/DWTAPI/Scanners` is used to list all available scanners. The response is an array of objects, each containing the scanner's name and ID.

### Fetching Available Scanners

```javascript
async function getDevices(host, scannerType) {
    devices = [];
    let url = host + '/DWTAPI/Scanners'
    if (scannerType != null) {
        url += '?type=' + scannerType;
    }

    try {
        let response = await axios.get(url)
            .catch(error => {
                console.log(error);
            });

        if (response.status == 200 && response.data.length > 0) {
            console.log('\nAvailable scanners: ' + response.data.length);
            return response.data;
        }
    } catch (error) {
        console.log(error);
    }
    return [];
}
```

### Supported Scanner Protocol Types
The supported [scanner protocol types](https://www.dynamsoft.com/web-twain/docs/info/api/Dynamsoft_Enum.html#dynamsoftdwtenumdwt_devicetype) include:
- TWAIN
- SANE
- ICA
- WIA
- ESCL

### Example: Listing TWAIN Scanners in an HTML Select Element
**JavaScript**

```javascript
let queryDevicesButton = document.getElementById("query-devices-button");
    queryDevicesButton.onclick = async () => {
        let scannerType = ScannerType.TWAINSCANNER | ScannerType.TWAINX64SCANNER;
        let devices = await getDevices(host, scannerType);
        let select = document.getElementById("sources");
        select.innerHTML = '';
        for (let i = 0; i < devices.length; i++) {
            let device = devices[i];
            let option = document.createElement("option");
            option.text = device['name'];
            option.value = JSON.stringify(device);
            select.add(option);
        };
    }
```

**HTML**
```html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Dynamsoft RESTful API Example</title>
    <link rel="stylesheet" href="main.css">
    <script src="https://unpkg.com/axios@1.6.7/dist/axios.min.js"></script>
</head>

<body>

    <button id="query-devices-button">Get Devices</button>
    <select id="sources"></select>
    <script src="main.js"></script>
</body>
</html>
```



## Step 3: Scanning Documents
To scan documents, specify the license key, scanner information, and other [scan parameters](https://www.dynamsoft.com/web-twain/docs/info/api/Interfaces.html#DeviceConfiguration) in a JSON object, then post the data to the `/DWTAPI/ScanJobs` endpoint.

### Scan Document

```javascript
let parameters = {
        license: license,
        device: JSON.parse(device),
};

parameters.config = {
    IfShowUI: false,
    PixelType: 2,
    //XferCount: 1,
    //PageSize: 1,
    Resolution: 200,
    IfFeederEnabled: false, // Set to true if you want to scan multiple pages
    IfDuplexEnabled: false,
};


let jobId = await scanDocument(host, parameters);

async function scanDocument(host, parameters, timeout = 30) {
    let url = host + '/DWTAPI/ScanJobs?timeout=' + timeout;

    try {
        let response = await axios.post(url, parameters)
            .catch(error => {
                console.log('Error: ' + error);
            });

        let jobId = response.data;

        if (response.status == 201) {
            return jobId;
        }
        else {
            console.log(response);
        }
    }
    catch (error) {
        console.log(error);
    }


    return '';
}
```

- The `timeout` parameter is optional. If the scanner does not respond within the specified time, the scan job will be canceled.
- The job ID is returned if the scan job is created successfully and can be used to retrieve the scanned images.


## Step 4: Obtaining the Scanned Images
Use the `/DWTAPI/ScanJobs/{jobId}/NextDocument` endpoint to retrieve the scanned images. 

```javascript
let url = host + '/DWTAPI/ScanJobs/' + jobId + '/NextDocument';

const response = await axios({
    method: 'GET',
    url: url,
    responseType: 'arraybuffer',
});
```

**Note**: Set the response type to `stream` in Node.js and `arraybuffer` in the browser.

To display the scanned document in a browser, create a `Blob` object from the array buffer and use the `URL.createObjectURL` method to generate a URL for an HTML image element.

```javascript
const arrayBuffer = response.data;
const blob = new Blob([arrayBuffer], { type: 'image/jpeg' });
const imageUrl = URL.createObjectURL(blob);
```

Repeat the request until the status code is not `200`. Store all scanned image URLs in an array and return them to the caller.

```javascript
async function getImages(host, jobId) {
    let images = [];
    let url = host + '/DWTAPI/ScanJobs/' + jobId + '/NextDocument';
    console.log('Start downloading images......');
    while (true) {
        try {
            const response = await axios({
                method: 'GET',
                url: url,
                responseType: 'arraybuffer',
            });

            if (response.status == 200) {
                const arrayBuffer = response.data;
                const blob = new Blob([arrayBuffer], { type: 'image/jpeg' });
                const imageUrl = URL.createObjectURL(blob);

                images.push(imageUrl);
            }
            else {
                console.log(response);
            }

        } catch (error) {
            // console.error("Error downloading image:", error);
            console.error('No more images.');
            break;
        }
    }

    return images;
}
```

Finally, you can display the scanned images as follows:

```javascript

<div class="row">
    <div class="full-img">
        <img id="document-image">
    </div>
</div>

<div class="row">
    <div class="thumb-bar" id="thumb-bar">
        <div class="thumb-box" id="thumb-box">
        </div>
    </div>
</div>

<script>
let images = await getImages(host, jobId, 'images');

for (let i = 0; i < images.length; i++) {
    let url = images[i];

    let img = document.getElementById('document-image');
    img.src = url;

    data.push(url);

    let option = document.createElement("option");
    option.selected = true;
    option.text = url;
    option.value = url;

    let thumbnails = document.getElementById("thumb-box");
    let newImage = document.createElement('img');
    newImage.setAttribute('src', url);
    if (thumbnails != null) {
        thumbnails.appendChild(newImage);
        newImage.addEventListener('click', e => {
            if (e != null && e.target != null) {
                let target = e.target;
                img.src = target.src;
            }
        });
    }
}
</script>
```

![scan documents via RESTful API in web](https://www.dynamsoft.com/codepool/img/2024/06/web-twain-restful-api-scan-document.jpg)

## Source Code
[https://github.com/yushulx/web-twain-document-scan-management/tree/main/examples/rest_api](https://github.com/yushulx/web-twain-document-scan-management/tree/main/examples/rest_api)