Scriptrunner REST Endpoint MultiPart Form Data

Daniel Garcia July 12, 2017

I'm trying to upload a file to a scriptrunner REST custom end point. The file upload is being initiated from a dialog box with the following code


(function ($) {
    $("#dialog-close-button").click(function (e) {
    $("#img-form").submit(function(e) {
        var fd = new FormData($("#img-form").get(0));
            type: "POST",
            data: fd,
            url: "/rest/scriptrunner/latest/custom/adminImgUploadDialogSave",
            dataType   : 'json',
            processData: false,
            contentType: false,
            headers: {
                'Content-Type' : 'multipart/form-data',
                'X-Atlassian-Token': 'no-check',
                'Access-Control-Allow-Origin' : '*'
        }).done(function(response) {
            if (response.status=="ok") {
        return false;

In the rest endpoint I have

import groovy.transform.BaseScript
import groovy.json.JsonBuilder
import javax.servlet.http.HttpServletRequest

@BaseScript CustomEndpointDelegate delegate

    httpMethod: "POST", groups: ["jira-administrators"]
) { MultivaluedMap queryParams, String body, HttpServletRequest req ->
    //if (filename==null) {
    //    return Response.ok(new JsonBuilder([status: 'not ok']).toString()).build()
    //} else {
    //    do stuff
    //    return Response.ok(new JsonBuilder([status: 'ok']).toString()).build()

body contains what I would expect

    Content-Disposition: form-data; name="atl_token"
    Content-Disposition: form-data; name="image"; filename="nodetype-child.png"
    Content-Type: image/png
    lots of binary data

but req.getParts() always returns an empty list.

How do I load/parse the form data?






3 answers

2 votes
Slava Dobromyslov March 26, 2023

First of all, I created a new class `StringMultiPartFormParser ` which parses the form and places all the plain form parameters into `parameters` and all files into `files`.

package mypackage

import org.apache.commons.fileupload.FileItem
import org.apache.commons.fileupload.FileUpload
import org.apache.commons.fileupload.UploadContext
import org.apache.commons.fileupload.disk.DiskFileItemFactory

import javax.servlet.http.HttpServletRequest

class StringMultiPartFormParser implements UploadContext {
private final String contentType
private final String characterEncoding
private final Long contentLength
private final String body

Map<String, String> parameters = [:]
Map<String, FileItem> files = [:]

StringMultiPartFormParser(HttpServletRequest request, String body) {
this.contentType = request.contentType
this.characterEncoding = request.characterEncoding
this.contentLength = request.contentLength
this.body = body

new FileUpload(new DiskFileItemFactory()).parseRequest(this).forEach { item ->
if (item.isFormField()) {
parameters.put(item.getFieldName(), item.getString())
} else {
files.put(item.getFieldName(), item)

long contentLength() {
return contentLength

String getCharacterEncoding() {
return characterEncoding

String getContentType() {
return contentType

int getContentLength() {
return -1

InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(body.getBytes())


And I use it in the REST endpoint like this:


package mypackage

import groovy.transform.BaseScript

import javax.servlet.http.HttpServletRequest

CustomEndpointDelegate delegate

httpMethod: 'POST',
groups: ['administrators']
) { MultivaluedMap queryParams, String body, HttpServletRequest request ->
final form = new StringMultiPartFormParser(request, body)
final someParameter = form.parameters.get('someParameter')
final file = form.files.get('file')

// Parameters and files validation goes here
// ...


return Response.status(200).entity([
someParameter: someParameter,
fileName: file.getName(),
fileContentType: file.getContentType(),
fileSize: file.getSize(),
1 vote
Jonny Carter
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
October 11, 2017

Hey, Daniel. So, after hacking with this at length, I concluded that there's not a really robust way to do this with ScriptRunner out of the box. Like you, I was able to hack together something that handled uploading basic text files, but images proved more problematic. I've submitted a bug report for that.

We may just end up documenting a working example or pointing people to how to roll their own REST Endpoint using JAX, but you can watch that for further developments on multipart support in ScriptRunner REST Endpoints.

Daniel Garcia June 11, 2018

I ended up doing this


import com.atlassian.jira.component.ComponentAccessor
import groovy.transform.BaseScript
import groovy.json.JsonBuilder
import javax.servlet.http.HttpServletRequest
import groovy.json.JsonSlurper
import java.util.regex.*

@BaseScript CustomEndpointDelegate delegate

TestImageUpload(httpMethod: "POST") { MultivaluedMap queryParams, String body, HttpServletRequest req ->
def params = new JsonSlurper().parseText(body)
Pattern re = Pattern.compile('^data:[^;]*;base64,(?<data>.*)$')
Matcher m1 = re.matcher((String)params['image'])
if (m1.matches()) {
String imageb64 ='data')
byte[] filedata = imageb64.decodeBase64()
String filename = '/var/something/somefile'
File file = new File(filename)
if (!file.exists()) {
FileOutputStream fos = new FileOutputStream(filename)
return Response.ok(new JsonBuilder([status: 'ok']).toString()).build()
} else {
return Response.ok(new JsonBuilder([status: 'not ok']).toString()).build()
Like # people like this
0 votes
Deleted user March 28, 2023

Im trying to do something similar, but in my case it must be a xlsx file. But payload in body has a corrupted encoding. Maybe you do know the reason? Didn't you faced an issue like this?

Only binary data is corrupted, headers are ok.

Jamie Echlin _ScriptRunner - The Adaptavist Group_
Marketplace Partner
Marketplace Partners provide apps and integrations available on the Atlassian Marketplace that extend the power of Atlassian products.
April 27, 2023

Hi - this wasn't possible until a recent release.

In the most recent releases we have added a new closure signature where we do not attempt to read the request body (and corrupt it for binary files).

A worked example is here:

Suggest an answer

Log in or Sign up to answer
AUG Leaders

Atlassian Community Events