Attachment API - excel getting uploaded but corrupted while downloading

Heena4
Kilo Contributor

I am invoking the https://<host>/api/now/attachment/upload service using HTTPUrlConnection

the uploaded file is being read via HTML file upload  <input type="file" name="file" id="file" multiple="true">

And AJAX call is used to invoke the servlet where I am sending request to upload service. The problem is I can see the attachment uploaded in ticket but I suspect as encoding is not correct, its not getting downloaded, Please help

 

public class MultipartUtility {
    private final String boundary;
    private static final String LINE_FEED = "\r\n";
    private HttpURLConnection httpConn;
    private String charset;
    private OutputStream outputStream;
    private PrintWriter writer;
   
    /**
     * This constructor initializes a new HTTP POST request with content type
     * is set to multipart/form-data
     *
     * @param requestURL
     * @param charset
     * @throws IOException
     */
    public MultipartUtility(String requestURL, String charset,String encoding)
            throws IOException {
        this.charset = charset;
        // creates a unique boundary based on time stamp
        boundary = "===" + System.currentTimeMillis() + "===";
        URL url = new URL(requestURL);
        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_IP, PROXY_PORT));
        System.out.println("URL : " + requestURL);
        httpConn = (HttpURLConnection) url.openConnection(proxy);
        httpConn.setUseCaches(false);
        httpConn.setDoOutput(true); // indicates POST method
        httpConn.setDoInput(true);
        httpConn.setRequestProperty("Content-Type",
                "multipart/form-data; boundary=" + boundary);
         httpConn.setRequestProperty("Authorization","Basic " + encoding);
          httpConn.setRequestProperty("Accept","application/json");
         httpConn.setRequestMethod("POST");
        outputStream = httpConn.getOutputStream();
        writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
                true);
    }
    /**
     * Adds a form field to the request
     *
     * @param name  field name
     * @param value field value
     */
    public void addFormField(String name, String value) {
        writer.append("--" + boundary).append(LINE_FEED);
        writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
                .append(LINE_FEED);
        writer.append("Content-Type: text/plain; charset=" + charset).append(
                LINE_FEED);
        writer.append(LINE_FEED);
        writer.append(value).append(LINE_FEED);
        writer.flush();
    }
    /**
     * Adds a upload file section to the request
     *
     * @param fieldName  name attribute in <input type="file" name="..." />
     * @param uploadFile a File to be uploaded
     * @param fileContent File inputStream
     * @throws IOException
     */
    public void addFilePart(String fieldName, String fileName, InputStream fileContent)
            throws IOException {
//        String fileName = uploadFile.getName();
        writer.append("--" + boundary).append(LINE_FEED);
        writer.append(
                "Content-Disposition: form-data; name=\"" + fieldName
                        + "\"; filename=\"" + fileName + "\"")
                .append(LINE_FEED);
//        writer.append(
//                "Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
                writer.append("Content-Type: application/octet-stream")
//                        + URLConnection.guessContentTypeFromName(fileName))
                .append(LINE_FEED);
          writer.append(LINE_FEED);
        writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
        writer.append(LINE_FEED);
        writer.flush();
 
//        try (FileInputStream inputStream = (FileInputStream)fileContent;) {
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
            while ((bytesRead = fileContent.read(buffer)) != -1) {
                System.out.println("bytes to read");
            outputStream.write(buffer, 0, bytesRead);
        }
        outputStream.flush();
//        }
        
        writer.append(LINE_FEED);
        writer.flush();   
    }
    /**
     * Adds a header field to the request.
     *
     * @param name  - name of the header field
     * @param value - value of the header field
     */
    public void addHeaderField(String name, String value) {
        writer.append(name + ": " + value).append(LINE_FEED);
        writer.flush();
    }
    /**
     * Completes the request and receives response from the server.
     *
     * @return a list of Strings as response in case the server returned
     * status OK, otherwise an exception is thrown.
     * @throws IOException
     */
    public String finish() throws IOException {
        StringBuffer response = new StringBuffer();
        writer.append(LINE_FEED).flush();
        writer.append("--" + boundary + "--").append(LINE_FEED);
        writer.close();
        // checks server's status code first
        int status = httpConn.getResponseCode();
        if (status == HttpURLConnection.HTTP_CREATED) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    httpConn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            httpConn.disconnect();
        } else {
            throw new IOException("Server returned non-OK status: " + status);
        }
        return response.toString();
    }
}
 
And the code used to invoked this utility
 
   
                String fileName = null;
                InputStream fileContent = null;
                ServletContext sc = request.getSession().getServletContext();
        String uploadPath = sc.getRealPath("") + File.separator + TMP_DIRECTORY;
                if (ServletFileUpload.isMultipartContent(request)) {
                    List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName())).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">
                    for (Part filePart : fileParts) {
                        fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
                        fileContent = filePart.getInputStream();
                        System.out.println("File Name in Attachment upload:" + fileName);
                    }
String authString = name + ":" + password;
                String encoding = Base64.getEncoder().encodeToString(authString.getBytes());
                String charset = "UTF-8";
                MultipartUtility multipart = new MultipartUtility(apiUrl, charset,encoding);
                multipart.addHeaderField("Content-Type", "multipart/form-data");
                multipart.addHeaderField("Accept", "application/json");
//                multipart.addHeaderField("Authorization", "Basic" + encoding);
                multipart.addFormField("table_name", "change_request");
                multipart.addFormField("table_sys_id", sysId);
                multipart.addFilePart("uploadFile", fileName, fileContent);
               
                String respons = multipart.finish();

                System.out.println("Attachment Upload Response:"+respons);

 

I am clueless as to where I am going wrong. On downloading file, am unable to view in excel, it says either file format not correct or corrupted.

 

Thanks,

Heena

3 REPLIES 3

Ankur Bawiskar
Tera Patron
Tera Patron

Hi Heena,

So basically from Java code you want to upload attachment?

Mark Correct if this solves your issue and also mark 👍 Helpful if you find my response worthy based on the impact.
Thanks
Ankur

Regards,
Ankur
Certified Technical Architect  ||  9x ServiceNow MVP  ||  ServiceNow Community Leader

Heena4
Kilo Contributor

Hi Ankur,

I resolved the issue myself, while I was able to upload csv and txt file, excel file (.xlsx) upload wasn't working. As per my understanding, the content-type and content-transfer-encoding was not going as required.

 

modified addFilePart method as follows

 

public void addFilePart(String fieldName, String fileName, InputStream fileContent)
throws IOException {
// String fileName = uploadFile.getName();
writer.append("--" + boundary).append(LINE_FEED);

writer.append(
"Content-Disposition: form-data; name=\"" + fieldName
+ "\"; filename=\"" + fileName + "\"")
.append(LINE_FEED);

writer.append(
guessContentTypeFromFile(fileName))
.append(LINE_FEED);

writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);

writer.append(LINE_FEED);

writer.flush();

byte[] content = IOUtils.toByteArray(fileContent);
outputStream.write(content);
outputStream.flush();
fileContent.close();
writer.flush();
}

 

public String guessContentTypeFromFile(String fileName) {
String contentType = URLConnection.guessContentTypeFromName(fileName);
if (contentType == null) {
return "application/octet-stream";
} else {
return contentType;
}
}

I can now upload an excel sheet with this code. However, the response received still shows content-type as "text/plain".

 

Thanks,

Heena

 

Hi Heena,

Thanks for sharing the update

Regards

Ankur

Regards,
Ankur
Certified Technical Architect  ||  9x ServiceNow MVP  ||  ServiceNow Community Leader