The Zurich release has arrived! Interested in new features and functionalities? Click here for more

Download file using REST API

Rj27
Mega Guru

Hi All,

I need to download files attached to any incident using REST API.

I will be sending incident detail from my postman. 

Already Referred to attachment api reference docs but could'nt find a solution.

NOTE: I don't have the option to use ui action 

Is this doable?

Thanks in Advance!

1 ACCEPTED SOLUTION

I'm executing python script from the command line.

I may be missing some steps below but the general way to install python is as follows.

1. Install python

https://phoenixnap.com/kb/how-to-install-python-3-windows

2. Create subdirectory

mkdir servicenowapis

3. Move to the subdirectory

cd servicenowapis

4. Create a virtualenv

python-m venv venv

5. Activate venv

venv\Scripts\activate.bat

6. Install request module

pip install requests

7. Copy the script I've provided to the directory

8. Copy and paste content of Constants.py over import statements

9. Edit username, password

9. Edit table_api.py to use Incident number

e.g.

get_file_attachment('INC0000002')

10. Execute command

python test_api.py

 

import requests

SERVICENOW_URL = 'https://<instance>.service-now.com'
SERVICENOW_USER = '<username>'
SERVICENOW_PWD = '<password>'

DOWNLOAD_DIR = './downloads/'

REQUEST_HEADER = {"json": {"Content-Type": "application/xml", "Accept": "application/json"},
                  "png": {"Content-Type": "application/xml", "Accept": "application/png"},
                  "xml": {"Content-Type": "application/xml", "Accept": "application/xml"},
                  }

ATTACHMENT_API = '/api/now/attachment'
TABLE_API = '/api/now/table'
# TABLE_NAME = 'kb_knowledge'
TABLE_NAME = 'incident'


def get_table_data(table_name, param):
    url = SERVICENOW_URL + TABLE_API + '/' + table_name + '?' + param
    response = requests.get(url, auth=(SERVICENOW_USER, SERVICENOW_PWD), headers=REQUEST_HEADER.get('json'))
    if response.status_code != 200:  # if error, then exit
        print('Status:', response.status_code, 'Headers:', response.headers, 'Error Response:', response.json())
        exit()
    return response.json()


def get_attachment_info(sys_id):
    url = SERVICENOW_URL + ATTACHMENT_API + '/' + sys_id
    response = requests.get(url, auth=(SERVICENOW_USER, SERVICENOW_PWD), headers=REQUEST_HEADER.get('json'))
    if response.status_code != 200:  # if error, then exit
        print(f'Status:  {response.status_code}, Headers:{response.headers}')
        exit()
    return response.json()


def get_attachment(att_sys_id, file_type, download_dir):
    url = SERVICENOW_URL + '/' + ATTACHMENT_API + '/' + att_sys_id + '/file'
    response = requests.get(url, auth=(SERVICENOW_USER, SERVICENOW_PWD), headers=REQUEST_HEADER.get(file_type))
    if response.status_code != 200:  # if error, then exit
        print('Status:', response.status_code, 'Headers:', response.headers, 'Error Response:', response.json())
        exit()
    with open(download_dir, 'wb') as f:
        for chunk in response:
            f.write(chunk)


def get_file_attachment(record_number):
    param = 'sysparm_query=number=' + record_number + '&sysparam_limit=1'

    file_info = get_table_data(TABLE_NAME, param).get('result')
    if len(file_info) < 1:
        print(f'There is no attachment to sys_id:{record_number}')
        return
    attachment_sys_id = file_info[0].get('sys_id')

    param = 'sysparm_query=table_sys_id=' + attachment_sys_id + '&sysparam_limit=1'
    kb_attachments = get_table_data('sys_attachment', param)
    result = kb_attachments.get('result')
    for attach_file in result:
        attach_sys_id = attach_file.get('sys_id')
        content_type = attach_file.get('content_type').split('/')
        file_ext = content_type[1]
        file_name = attach_file.get('file_name')
        get_attachment(attach_sys_id, file_ext, DOWNLOAD_DIR + file_name)


if __name__ == '__main__':
    # get_file_attachment('KB0010062')  # knowledge base number
    get_file_attachment('INC0000002')

View solution in original post

26 REPLIES 26

Hi Hozawa,

Thanks for the clarification!


Tried this in my PDI and this seems to be working fine.

However since this involves downloading python, i am checkin for other way as well as if we look from end user's perspective not everyone will have python installed.

It seems a new question with the additional requirements was created already.

Would appreciate if this question would be closed now because the original question was answered. If I can develop a Scripted REST API to down files, I'll reply in the new thread.

Thanks.

Hi Hitoshi,

Since i didnot mention in my question anything related to python, marking this as correct as it worked for me in my PDI.

Thanks!

Hi Hitoshi,

 

I'm getting errors as the below while running this code. Seems attachments are not found in mentioned directory. Is that changed now?

 

line 71, in <module>
get_file_attachment('INC0010003')
line 66, in get_file_attachment
get_attachment(attach_sys_id, file_ext, DOWNLOAD_DIR + file_name)
line 44, in get_attachment
with open(download_dir, 'wb') as f:
FileNotFoundError: [Errno 2] No such file or directory: './downloads/test.txt'

Hitoshi Ozawa
Giga Sage
Giga Sage

To anyone who knows flask, it's possible to wrap the script in flask so it can be executed by calling an URL.

e.g. http://127.0.0.1:8080/downloadattachments?number=INC0000002

from flask import request

from servicenowapis import app
from servicenowapis.table_api import get_file_attachment

@app.route('/')
@app.route('/index')
def index():
    """
    Sample home page used to test connectivity
    :return: Sample string text to show the site is working.
    created: 2020/0709  Hitoshi Ozawa
    """
    message = "<html><body><h1>ServiceNow APIs</h1></body></html>"
    return message


@app.route('/downloadattachments')
def download_attachments():
    ticket_number = request.args.get('number')
    get_file_attachment(ticket_number)
    return "Downloaded attachments to ticket number:" + ticket_number