Skip to content

Instantly share code, notes, and snippets.

@bvlion
Created March 12, 2017 07:52
Show Gist options
  • Save bvlion/93d708317bba9846de4e29674c5fd003 to your computer and use it in GitHub Desktop.
Save bvlion/93d708317bba9846de4e29674c5fd003 to your computer and use it in GitHub Desktop.
SlackにJavaからBinaryを送信するサンプル
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.FastDateFormat;
/**
* SlackにJavaからBinaryを送信する
* @author Bvlion
*/
public class SlackBinaryPost {
/** 改行コード */
private static final String CRLF = "\r\n";
/** バウンダリのヘッダ(ハイフン2つ) */
private static final String BOUNDARY_HEADER = "--";
/** ファイルアップロード先 */
private static final String SLACK_POST_URL = "https://slack.com/api/files.upload";
/** 読み込みタイムアウト値 */
private static final int READ_TIMEOUT = 10 * 1000;
/** 接続タイムアウト値 */
private static final int CONNECTION_TIMEOUT = 10 * 1000;
/** HttpURLConnection */
private HttpURLConnection con;
/** バウンダリの本体(ハイフン2つ以降) */
private String boundaryBody;
/** ファイル名 */
private String fileName;
/** 送信データ(byte配列) */
private byte[] fileData;
/** タイトル等を入れるマップ */
private Map<String, String> textDataMap = new HashMap<>();
/**
* コンストラクタ
* @param builder インナークラスBuilder
* @throws IOException
*/
SlackBinaryPost(Builder builder) throws IOException {
this.boundaryBody = "*****" + UUID.randomUUID().toString() + "*****";
this.con = (HttpURLConnection) new URL(SLACK_POST_URL).openConnection();
this.textDataMap = builder.textDataMap;
this.fileData = builder.fileData;
this.fileName = builder.fileName;
createConnection();
}
/**
* HttpURLConnectionを生成する
* @throws ProtocolException
*/
private void createConnection() throws ProtocolException {
this.con.setRequestMethod("POST");
this.con.setDoOutput(true);
this.con.setDoInput(true);
this.con.setUseCaches(false);
this.con.setReadTimeout(READ_TIMEOUT);
this.con.setConnectTimeout(CONNECTION_TIMEOUT);
this.con.setRequestProperty("Connection", "Keep-Alive");
this.con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + this.boundaryBody);
}
/**
* 送信する
* @return レスポンス
*/
public String post() {
String data = StringUtils.EMPTY;
try {
write();
data = read();
} catch (IOException e) {
e.printStackTrace();
}
this.con.disconnect();
return data;
}
/**
* データの書き込みを行う
* @throws IOException
*/
private void write() throws IOException {
try (DataOutputStream request = new DataOutputStream(this.con.getOutputStream())) {
request.writeBytes(BOUNDARY_HEADER + this.boundaryBody + CRLF);
request.writeBytes("Content-Disposition: form-data; name=\"file\";filename=\"" + this.fileName + "\"" + CRLF);
request.writeBytes(CRLF);
request.write(this.fileData);
request.writeBytes(CRLF);
// テキストデータの設定
for (Map.Entry<String, String> entry : this.textDataMap.entrySet()) {
request.writeBytes(BOUNDARY_HEADER + this.boundaryBody + CRLF);
request.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + CRLF);
request.writeBytes("Content-Type: text/plain" + CRLF);
request.writeBytes(CRLF);
request.write(entry.getValue().getBytes(StandardCharsets.UTF_8.toString()));
request.writeBytes(CRLF);
}
request.writeBytes(BOUNDARY_HEADER + this.boundaryBody + BOUNDARY_HEADER + CRLF);
}
}
/**
* レスポンスを読み込む
* @return レスポンスボディ
* @throws UnsupportedEncodingException
* @throws IOException
*/
private String read() throws UnsupportedEncodingException, IOException {
StringBuilder str = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(this.con.getInputStream(), StandardCharsets.UTF_8.toString()))) {
String line;
while ((line = br.readLine()) != null) {
str.append(line);
str.append(System.lineSeparator());
}
}
return str.toString();
}
/** Builder */
public static class Builder {
/** token等のテキストデータ */
Map<String, String> textDataMap = new HashMap<>();
/** ファイルのバイトデータ */
byte[] fileData;
/** ファイル名 */
String fileName;
/**
* tokenを設定する
* @param token トークン
* @return Builder
*/
public Builder token(String token) {
this.textDataMap.put("token", token);
return this;
}
/**
* 送信するチャンネル名を設定する
* @param channels チャンネル
* @return Builder
*/
public Builder channels(String channels) {
this.textDataMap.put("channels", channels);
return this;
}
/**
* イメージのタイトルを設定する
* @param title イメージのタイトル
* @return Builder
*/
public Builder title(String title) {
this.textDataMap.put("title", title);
return this;
}
/**
* バイトデータを設定する
* @param fileData バイトデータ
* @return Builder
*/
public Builder fileData(byte[] fileData) {
this.fileData = fileData;
return this;
}
/**
* ファイル名を設定する
* @param fileName ファイル名
* @return Builder
*/
public Builder fileName(String fileName) {
this.fileName = fileName;
return this;
}
/**
* SlackBinaryPostのインスタンスを生成する
* @return SlackBinaryPostインスタンス
* @throws IOException
*/
public SlackBinaryPost build() throws IOException {
if (this.fileData == null || this.textDataMap.size() != 3) {
throw new IllegalArgumentException("必要データが登録されていません。");
}
if (StringUtils.isBlank(this.fileName)) {
this.fileName = FastDateFormat.getInstance("yyyyMMddHHmmss").format(Calendar.getInstance());
}
return new SlackBinaryPost(this);
}
}
}
@bvlion
Copy link
Author

bvlion commented Mar 12, 2018

dependencyにcommons-langが必要です。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment