개요
Apache Http Client를 통해 웹페이지에서 파일업로드를 하듯이 파일전송도 할 수 있습니다. 간단한 전송 예제코드를 보이고, 주의점을 설명합니다.
(방법이 어렵진 않으나 모르면 발생하는 오류를 해결하기 어려워 이 문서를 작성합니다.)
예제코드
public void upload(
final Map<String,Object> paramMap,
final Properties prop) {
HttpEntity multipartEntity = null;
try {
// <패스|쿼리|바디 필수값 검증>
final String filePath = (String) paramMap.get("filePath");
if (StringUtils.isBlank(filePath)) {
throw new IllegalArgumentException("필수값 누락 : filePath");
}
final File file = new File(filePath);
if (!file.exists()) {
throw new IllegalArgumentException("필수파일 누락. file.getAbsolutePath():" + file.getAbsolutePath());
}
// </패스|쿼리|바디 필수값 검증>
if (log.isDebugEnabled()) {
log.debug("File.separator:" + File.separator);
}
String fileName = (String) paramMap.get("fileName");
if (StringUtils.isBlank(fileName)) {
fileName = filePath.substring(filePath.lastIndexOf(File.separator) + 1);
}
final String url = prop.getProperty("URL");
multipartEntity = MultipartEntityBuilder.create()
.addBinaryBody("file", file, ContentType.MULTIPART_FORM_DATA, fileName).build();
// <http-method="POST">
final HttpUriRequest httpReq = new HttpPost(url);
// </http-method="POST">
// httpReq.setHeader("Content-Type","multipart/form-data"); // !주의! 'Content-Type'을 직접 지정하지 말고, MultipartEntityBuilder에서 자동으로 생성되는 대로 사용할 것.
httpReq.setHeader("Accept","*/*");
httpReq.setHeader("Accept-Encoding","gzip, deflate, br");
// <http-method="POST" "multipart/form-data">
((HttpEntityEnclosingRequestBase) httpReq).setEntity(multipartEntity);
// </http-method="POST" "multipart/form-data">
int timeoutSecond = 8;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeoutSecond * 1000)
.setConnectionRequestTimeout(timeoutSecond * 1000)
.setSocketTimeout(timeoutSecond * 1000).build();
CloseableHttpClient httpclient = HttpClientBuilder.create()
.setDefaultRequestConfig(config).build();
httpclient.execute(httpReq);
} catch (final Throwable t) {
final String message = "API 파일업로드 실패.";
throw new RuntimeException(message + " iParam:" + paramMap + ", prop:" + prop, t);
}
}
중요한 코드 위주로 간략화시키려하다보니 프로젝트 코드와 차이가 있는 편입니다. 핵심 내용만 참고하고, 응용할 필요가 있습니다.
해설
전송 방식은 POST, 파일데이터는 ‘MultipartEntityBuilder’를 이용해 추가합니다. 헤더에는 “Content-Type”을 직접 입력하지 않도록 주의합니다.(이유는 아래 [주의점]에서 후술.)
그 외에는 일반적인 POST와 동일합니다.
주의점
MultipartEntityBuilder를 사용할 때, 아래와 같이 직접 헤더에 "multipart/form-data"로 입력하면 안 됩니다.
httpReq.setHeader("Content-Type","multipart/form-data");
위와 같이 코딩했다면, 응답으로 '400 Bad Request'를 받게 될 것입니다.
왜냐하면 파일 데이터 전송 시 boundary도 필요하기 때문입니다. boundary는 파일데이터와 다른 데이터를 구분하는 경계로 사용하며, 이 boundary가 아래처럼 Content-Type에 함께 있어야 합니다.
Content-Type : multipart/form-data; boundary=(자동생성)
다만, MultipartEntityBuilder를 사용하여 Entity를 입력하면 이러한 헤더를 자동으로 입력해주므로 boundary를 어떻게 입력할 것인지 고민하지 않아도 됩니다.
참고자료
- HTTP multipart/form-data 이해하기 : boundary가 어떻게 사용되는지 자세한 설명을 볼 수 있습니다.
<끝> [DEV240827a]