Bulk import attachments to Jira server issues via REST API
Platform Notice: Data Center Only - This article only applies to Atlassian products on the Data Center platform.
Note that this KB was created for the Data Center version of the product. Data Center KBs for non-Data-Center-specific features may also work for Server versions of the product, however they have not been tested. Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.
*Except Fisheye and Crucible
Summary
Learn an automated way to quickly import many attachments to many JIRA issues.
For example, if you've imported issues from another JIRA using CSV, but were unable to add the attachments to the CSV. If the issue keys were not modified (you included them on the import), you can add the attachments this way.
You can attach a single file in a single JIRA issue using REST API. However, as the number of attachments grow, this will become harder and more error prone.
It's important to point out that these steps were only tested in JIRA 6.4 and may not work in other versions. Please, test it first.
If the project you're importing attachments from ever had its project key changed, the backup files will contain the old project key and this KB is not going to work without changes.
Some data will not be imported
This import does not include metadata about the attachment, such as: author, created date, etc. If this is important do not use this KB.
Solution
Pre-requisites
To use the following steps, make sure the following prerequisites are met (otherwise, it will simply not work).
You have access to a user who can add the attachments to all issues
You have a computer with access to your JIRA and it's running Linux or Mac. The OS running the JIRA Server doesn't matter, but you'll have to run a bash script that won't work on Windows
You have all the attachments in the following directory structure (the one used by JIRA):
ABC
ABC-1
attachment1.png
attachment2.jpg
ABC-2
attachment3.pdf
On newer versions, Jira will have an extra directory in the directory structure like below. The 10000 and 20000 refers to groupings of issue ID. More details about it on Locate Jira file attachments in the filesystem.
ABC
10000
ABC-1
attachment1.png
attachment2.jpg
ABC-2
attachment3.pdf
20000
ABC-3
attachment4.pdf
ABC-4
attachment5.png
Where ABC is the project key and ABC-1 and ABC-2 are issue keys in this project.
Each attachment must be within a directory with the issue key the attachment will be added to, which in its turn must be within a directory with the project key.\
Fixing File Names (only if exporting from another JIRA)
If you're importing this from another bug tracker and the filenames are correct, skip this section and go directly to the 'Procedure' section.
This directory structure was not chosen randomly. It's exactly the one used by JIRA to hold the attachments, so you can use this easily to migrate to another JIRA, simply using the attachments in JIRA's 'attachments' directory.
However, if this is the case, JIRA doesn't keep the files with their names, it keeps the files with filenames as their IDs in the database. JIRA keeps the original file name in the database.
If you go directly to the 'Procedure' section, the attachments will be imported with the file ID as their name. Also, they will have other ID as their filename in the destination directories: a mess.
Follow these instructions first to change the file names to their correct ones.
Pre-requisites
Access to the source JIRA's DB
Access to the source JIRA's attachments
Be aware that this was only tested in Postgres database.
Instructions
Find the project directory (ABC) under the 'attachments' folder
Copy the directory elsewhere (we will only change the copy, not to affect the source JIRA Server)
If you're using a Windows JIRA Server, you'll need to copy the attachments to a Linux or Mac machine.
If you make any of the following changes to the actual JIRA attachments directory instead of a copy, this will make your JIRA Server no longer recognize the attachments.
Run the following select in the source JIRA Server database to generate a few commands
If you're importing from a Jira Cloud instance then contact Atlassian Support to run the query below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/* Example for PostgreSQL */ SELECT ( 'mv ' || project.originalkey || '-' || jiraissue.issuenum || '/' || fileattachment.id || ' "' || project.originalkey || '-' || jiraissue.issuenum || '/' || filename || '"' ) AS command FROM fileattachment JOIN jiraissue ON fileattachment.issueid = jiraissue.id JOIN project ON jiraissue.project = project.id WHERE project.pkey = 'ABC'; /* Example for SQL Server and/or MySQL */ SELECT ( CAST('mv ' AS nvarchar(18)) + CAST(<jiraschema>.project.originalkey AS nvarchar(18)) + '' + CAST(<jiraschema>.jiraissue.issuenum AS nvarchar(18)) + '/' + CAST(<jiraschema>.fileattachment.ID asASnvarchar(18)) + ' "' + <jiraschema>.project.originalkey + '' + CAST(<jiraschema>.jiraissue.issuenum AS nvarchar(18)) + '/' + filename + '"' ) AS command FROM <jiraschema>.fileattachment JOIN <jiraschema>.jiraissue ON <jiraschema>.fileattachment.issueid = <jiraschema>.jiraissue.id JOIN <jiraschema>.project ON <jiraschema>.jiraissue.project = <jiraschema>.project.id WHERE <jiraschema>.project.pkey = 'ABC' /* Example for MySQL 8.x */ SELECT CONCAT(CAST('mv ' AS CHAR(18)), CAST(<jiraschema>.project.ORIGINALKEY AS CHAR(18)), '-', CAST(<jiraschema>.jiraissue.issuenum AS CHAR(18)), '/', CAST(<jiraschema>.fileattachment.ID as CHAR(18)), ' "', <jiraschema>.project.originalkey, '-', CAST(<jiraschema>.jiraissue.issuenum AS CHAR(18)), '/', filename, '"') AS command FROM <jiraschema>.fileattachment JOIN <jiraschema>.jiraissue ON <jiraschema>.fileattachment.issueid = <jiraschema>.jiraissue.id JOIN <jiraschema>.project ON <jiraschema>.jiraissue.project = <jiraschema>.project.id WHERE <jiraschema>.project.pkey = 'ABC' /* Example for Oracle */ SELECT ( CAST('mv ' AS VARCHAR2(18)) || CAST(project.originalkey AS VARCHAR2(18)) || '-' || CAST(jiraissue.issuenum AS VARCHAR2(18)) || '/' || CAST(fileattachment.ID as VARCHAR2(18)) || ' "' || project.originalkey || '-' || CAST(jiraissue.issuenum AS VARCHAR2(18)) || '/' || filename || '"' ) AS command FROM fileattachment JOIN jiraissue ON fileattachment.issueid =jiraissue.id JOIN project ON jiraissue.project = project.id WHERE project.pkey = 'ABC';
Replace 'ABC' by the project key
This query contains two backslashes used to space the double quotation marks. Depending on the database and version, it may need to be removed, replacing <\"> by <">
In the example for SQL Server and/or MySQL, replace
<jiraschema>
by the proper Jira Schema of your systemHere are the sample results for the above query (PostgreSQL)
1 2
mv ABC-1/10000 "ABC-1/image1.png" mv ABC-1/10001 "ABC-1/script.sh"
Navigate to the copy project directory in the Linux/Mac server
Run the 'mv' commands generated by the select. This steps will rename all the existing attachments (ID) to their respective name
Remove the 'thumbs' directory from the copy directory as we don't want to also add the thumbnails.
1
2
3
4
cd ABC
## If you have the directory structure with the 10000 directory after ABC such as ABC/10000 you'll need to run this instead and repeat for others like 20000:
## cd ABC/10000
rm -rf ./ABC-*/thumbs
Procedure
Follow these instructions to bulk add all attachments to all issues at once. This can only be done to a single project, but you can run it more than once if you need to add to more.
If you followed the instructions in 'Fixing Files Names', please use the directory with the fixed attachments.
Atlassian Cloud only: Create an API token here;
Navigate to the project directory:
1 2 3
cd ABC ## If you have the directory structure with the 10000 directory after ABC such as ABC/10000 you'll need to run this instead and repeat the process for others like 20000: ## cd ABC/10000
Copy the following code block into a file to the project directory (ABC, for example)
jira_attachments_import.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#!/bin/bash # Author: Jaime Kirch da Silveira (Atlassian Cloud Support) # Last update: April, 17th, 2015 # Updated by Kumar Pillai (Atlassian Cloud Support) - 24 May 2019 # Added functionality to authenticate using email address and API token. # This will import all attachments to JIRA issues # Check this KB for more information: # https://confluence.atlassian.com/display/JIRAKB/Bulk+import+attachments+to+JIRA+issues+via+REST+API if [[ $# != 4 ]] then # echo "Format: $0 <username> <password> <project key> <JIRA URL>" echo "Format: $0 <email address> <token> <project key> <JIRA URL>" echo "Please notice that the JIRA URL must include all the path to access JIRA, including anything after the '/' (like /jira) and the protocol as well (like https://)" exit fi USERNAME=$1 PASSWORD=$2 PROJECT_KEY=$3 JIRA_URL=$4 AUTH=`echo -n ${USERNAME}:${PASSWORD} | openssl base64` #echo -n '$USERNAME:$PASSWORD' # AUTH_TYPE=token # AUTH_TYPE=cookie AUTH_TYPE=token COOKIE_FILE=cookie.txt if [ "${AUTH_TYPE}" = 'cookie' ] then curl --cookie-jar ${COOKIE_FILE} -H "Content-Type: application/json" -d '{"username":"'${USERNAME}'", "password":"'${PASSWORD}'" }' -X POST ${JIRA_URL}/rest/auth/1/session fi for key in ${PROJECT_KEY}-* do if [ "$(ls -A ${key})" ] then echo "Importing attachments for issue $key" for file in $key/* do echo "Importing file: $file" if [ "${AUTH_TYPE}" = 'cookie' ] then curl -D- -b ${COOKIE_FILE} -X POST --header "X-Atlassian-Token: no-check" -F "file=@${file}" ${JIRA_URL}/rest/api/2/issue/${key}/attachments elif [ "${AUTH_TYPE}" = 'basic' ] then curl -D- -u ${USERNAME}:${PASSWORD} -X POST --header "X-Atlassian-Token: no-check" -F "file=@${file}" ${JIRA_URL}/rest/api/2/issue/${key}/attachments elif [ "${AUTH_TYPE}" = 'token' ] then curl -D- -H "Authorization: Basic ${AUTH}" -X POST --header "X-Atlassian-Token: no-check" -F "file=@${file}" ${JIRA_URL}/rest/api/2/issue/${key}/attachments fi done fi done
Give the file executable permissions
1
chmod a+x jira_attachments_import.sh
Run it
1
./jira_attachments_import.sh <user account> <password> ABC https://charliesjira.atlassian.net | tee Jira-IMPORT.txt
Atlassian Cloud:
Enter your email address in the
<user account>
Enter the API token in the
<password>
Server/DC:
Enter your username in the
<user account>
(tests worked using Jira Internal user account with administration + project rights)Enter the usernames's password in the
<password>
project key (it doesn't retrieve it from the directory yet) and JIRA URL (https://charliesjira.atlassian.net);
tee command after the pipe will export the results to both terminal and a text file. It can be used later for troubleshooting errors and/or provided to Atlassian Support
The attachments will be imported one by one. You can watch for errors.
The expected return for each attachment if it's successful:
HTTP/1.1 200 OK
Was this helpful?