Upload a file to server from an add-on admin page

Hi Experts!

I’m developing an add-on for JIRA that needs a file to be uploaded. The file contains rules in JSON format that defines the behaviour of the add-on.
I’ve defined a multipart-config servlet and a multipart/form-data form in admin page. But I can’t retrieve any part with getPart method at servlet side, it seems to arrive “empty”, like no form was defined.

These are some pieces of my code:

upload rules template (vm):

...
<form id="upload-rules" class="aui" method="POST" enctype="multipart/form-data">
	<input type="hidden" id="uploading" name="uploading" value="y">
	<input type="hidden" id="test" name="test" value="y">

	<div class="field-group">
		<label for="file">Rules config file<span class="aui-icon icon-required">(required)</span></label>
		<input type="file" id="file" name="file" class="file">
	</div>

	<div class="field-group">
		<input type="submit" value="Upload" class="button" name="upload" id="upload">
		<a href="$baseURL/plugins/servlet/upm">Cancel</a>		     
	</div>

</form>
...

RulesServlet.java

...
@Named("RulesServlet")
@MultipartConfig
public class RulesServlet extends HttpServlet
{
	...
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
	{
    	LOG.debug("doGet method called.");

    	//Only admin users are admitted
		if (!isAdminUser(request)) {
			redirectToLogin(request, response);
			return;
		}
		AutoEscalateConfig autoEscalateConfig = null;		

		...stuff for getting configs...

        if ("preupload".equals(request.getParameter("op"))) {

        	Map<String, Object> context = getContextWithCommonParams(autoEscalateConfig);

        	// Render the template with the issue inside the context
        	response.setContentType(CONTENT_TYPE);
        	renderer.render(UPLOAD_TEMPLATE, context, response.getWriter());
        }
	}
	
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    	LOG.debug("doPost method called.");
     
		//Only admin users are admitted
		if (!isAdminUser(request)) {
			redirectToLogin(request, response);
			return;
		}

		try {
			LOG.debug("Updating rules...");
			
			LOG.debug("Request content type: " + request.getContentType());
			LOG.debug("Request parameterMap size: " + request.getParameterMap().size());
			LOG.debug("Request parts size: " + request.getParts().size());
			
			Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
			String fileName = Paths.get(getSubmittedFileName(filePart)).getFileName().toString(); // MSIE fix.
			InputStream fileContent = filePart.getInputStream();	        	

			...stuff to get rules from file...

			response.setContentType(CONTENT_TYPE);
			renderer.render(UPLOAD_TEMPLATE, context, response.getWriter());

		} catch (Exception e) {
			LOG.error("Error creating escalate rules.", e);
			throw new ServletException(e);
		}

    }	
	
	private static String getSubmittedFileName(Part part) {
	    for (String cd : part.getHeader("content-disposition").split(";")) {
	        if (cd.trim().startsWith("filename")) {
	            String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
	            return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
	        }
	    }
	    return null;
	}	
	
}
...

atlassian-plugin.xml

...
<servlet key="mim-autoescalate-upload-rules-servlet" i18n-name-key="mim-autoescalate-rules-upload-servlet.name" 
		class="cat.palma.jira.escalate.ui.RulesServlet" name="MIM Autoescalate Plugin Upload Rules Servlet">
	<description key="mim-autoescalate-rules-upload-servlet.description">The rules upload servlet to populate the MIM Autoescalate Plugin rule table</description>
	<url-pattern>/mimautoescalateplugin/rules</url-pattern>
</servlet>
...
<web-item key="mim-autoescalate-link-upload-rules" i18n-name-key="mim-autoescalate-link-upload-rules.name"
		name="MIM Autoescalate Plugin Setup Rules Link" section="admin_plugins_menu/mim-autoescalate-section" weight="1010">
	<description key="mim-autoescalate-link-upload-rules.description">The Setup Page Link for escalate rules of MIM Autoescalate Plugin</description>
	<label key="mim-autoescalate-link-upload-rules.label"></label>
	<link linkId="mim-autoescalate-link-upload-rules-link">/plugins/servlet/mimautoescalateplugin/rules?op=preupload</link>
</web-item>
...

pom.xml

...
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>servlet-api</artifactId>
	<version>2.4</version>
	<scope>provided</scope>
</dependency>
...
<properties>
	<jira.version>7.7.1</jira.version>
	<amps.version>6.3.15</amps.version>
	<plugin.testrunner.version>1.2.3</plugin.testrunner.version>
	<atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
	<maven.compiler.source>1.8</maven.compiler.source>
	<maven.compiler.target>1.8</maven.compiler.target>
	<atlassian.template.renderer.version>2.0.0</atlassian.template.renderer.version>
	<activeobjects.version>1.5.0</activeobjects.version>
	<spring.version>4.2.5.RELEASE</spring.version>
	<gson.version>2.3.1</gson.version>
...
</properties>
...

After picking up a file and press upload button, following messages from servlet debug appear in log:

[INFO] [talledLocalContainer] 2018-05-17 09:00:55,820 DEBUG admin [c.p.j.escalate.ui.RulesServlet] doPost method called.
[INFO] [talledLocalContainer] 2018-05-17 09:00:55,821 DEBUG admin [c.p.j.escalate.ui.RulesServlet] User performing action admin.
[INFO] [talledLocalContainer] 2018-05-17 09:00:55,822 DEBUG admin [c.p.j.escalate.ui.RulesServlet] Updating rules...
[INFO] [talledLocalContainer] 2018-05-17 09:00:55,822 DEBUG admin [c.p.j.escalate.ui.RulesServlet] Request content type: multipart/form-data; boundary=----WebKitFormBoundaryxRnbEsLcgjpzyKWn
[INFO] [talledLocalContainer] 2018-05-17 09:00:55,822 DEBUG admin [c.p.j.escalate.ui.RulesServlet] Request parameterMap size: 1
[INFO] [talledLocalContainer] 2018-05-17 09:00:55,823 DEBUG admin [c.p.j.escalate.ui.RulesServlet] Request parts size: 0
[INFO] [talledLocalContainer] 2018-05-17 09:00:55,823 ERROR admin [c.p.j.escalate.ui.RulesServlet] Error creating escalate rules.
[INFO] [talledLocalContainer] java.lang.NullPointerException

ParameterMap contains only “op” parameter from link ("…/plugins/servlet/mimautoescalateplugin/rules?op=preupload").
request.getParts().size() is 0, so request.getPart(“file”) returns null and no file is uploaded. Seems like no form was defined.

Does anyone has any idea about what’s going on or where error is?

Thanks in advance!!


Leirbag A.

1 Like

I observed the same behavior and used the workaround described here.

You need to add another dependency in you pom.xml.

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
    <scope>compile</scope>
</dependency>

Then you can access the upload.

final DiskFileItemFactory factory = new DiskFileItemFactory();
final ServletFileUpload upload = new ServletFileUpload(factory);
upload.setSizeMax(20 * 1024 * 1024L);
final List<FileItem> fileItems = upload.parseRequest(request);
final FileItem file = fileItems.get(0); // assuming only one file is uploaded
try (InputStream source = file.getInputStream()) {
    …
}

Hi @SandstormMediaGmbH,

Thanks for your feedback. It was just the missing piece.

Regards!

Hi @leirbag

Need help. Im also trying to upload file, im also getting null pointer. Can you please help me with missing piece. Thank You