How can I use purely java.net to attach a file to a task using the Asana API?
Specifically, how do I make a well formed HTTP POST multipart/form-data encoded request to the Asana API in order to successfully attach a file to a task?
The Asana API expects attachments to be uploaded to tasks via an HTTP POST request with a multipart/form-data encoded file:
https://asana.com/developers/api-reference/attachments#upload
As explained in this answer regarding the HTTP spec on line breaks, ensure that you are using \r\n
line breaks as Java will convert most println()
methods to the platform dependent line.separator
and the Asana servers may not tolerate poorly formed line breaks.
A well formed multipart/form-data POST request will look something like this:
Authorization: Basic <BASE64_ENCODED_AUTH>
Content-Type: multipart/form-data; boundary=14d07d7cbcf
User-Agent: Java/1.7.0_76
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 141
--14d07d7cbcf
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
A file
--14d07d7cbcf--
This is an implementation in Java which purely uses java.net classes to manually construct a well formed multipart/form-data encoded POST request for the Asana attachments API:
import org.apache.commons.codec.binary.Base64;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
public class AttachFileToTask {
// Use HTTP compliant line feeds in the request.
// Note that Java println() methods may use platform dependent line feeds.
private static String LINE_FEED = "\r\n";
public static void main(String[] args) throws Exception {
// Task attachments endpoint
String url = "https://app.asana.com/api/1.0/tasks/<TASK_ID>/attachments";
File theFile = new File("/path/to/file.txt");
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
// Set basic auth header
String apiKey = "<API_KEY>" + ":";
String basicAuth = "Basic " + new String(new Base64().encode(apiKey.getBytes()));
connection.setRequestProperty("Authorization", basicAuth);
// Indicate a POST request
connection.setDoOutput(true);
// A unique boundary to use for the multipart/form-data
String boundary = Long.toHexString(System.currentTimeMillis());
// Construct the body of the request
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
PrintWriter writer = null;
try {
writer = new PrintWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8"));
String fileName = theFile.getName();
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"").append(LINE_FEED);
writer.append("Content-Type: text/plain").append(LINE_FEED);
writer.append(LINE_FEED);
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(theFile), "UTF-8"));
for (String line; (line = reader.readLine()) != null; ) {
writer.append(line).append(LINE_FEED);
}
} finally {
if (reader != null) try {
reader.close();
} catch (IOException logOrIgnore) {
}
}
writer.append(LINE_FEED);
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
writer.close();
} catch (Exception e) {
System.out.append("Exception writing file" + e);
} finally {
if (writer != null) writer.close();
}
System.out.println(connection.getResponseCode()); // Should be 200
System.out.println(connection.getResponseMessage());
}
}
Please note that basic authentication using the API Key is discouraged outside of personal use or development of utility scripts. When deploying production applications for multiple users, please use Asana Connect (OAuth 2.0)