2014년 11월 25일 화요일

[Android] Multipart를 이용하여 이미지, 문자열 등 다른 값 한 번에 전송하기(POST) [출처] [Android] Multipart를 이용하여 이미지, 문자열 등 다른 값 한 번에 전송하기(POST)|작성자 LifeClue

펌위치 : http://blog.naver.com/legendx/40132716891

모바일 네트워크에서 서버로 보내는 자료는 일반적으로 문자열이지만 간혹 이미지를 전송해야 하는 경우가 있다.
Content-Type을 Multipart로 설정하면 byte 전송이 가능한데, 이 때 이미지를 서버로 전송할 수 있다.
Multipart를 구현한 지는 몇달 되었다. 그 때 당시 검색할 땐 Multipart로 이미지 전송하는 글들을 볼 수 있있지만, 문자열과 혼합된 여러 자료를 넘기는 방법은 소개되지 않았던 것으로 안다.
물론 시간이 지난 지금에서야 방법이 소개되었는지는 모르겠지만, 같은 고민을 하는 이들에게
작은 도움이 되고자 포스팅하려 한다.

Multipart는 말 그대로 여러 유형의 데이터를 전송할 때 사용되는 Content-Type 중 하나다.
본인도 본래는 서버로 문자열만 전송하다가 이미지를 전송하게 되어 사용하게 되었다.
자바에서는 HttpClient와 URLConnection이 있는데, 둘 다 잘 아는 것은 아니지만 우선 본인은
URLConnection을 사용하였다. HttpClient는 고려도 안해봤다. 그러나 byte로 전송을 해야 하기 때문에
URLConnection이 적절하다고 생각한다. HttpClient는 byte전송이 안되는 것으로 안다. 물론 본인이 틀렸을 수도 있다.

URL url = new URL("http://localhost/multipart.php");
String boundary = "SpecificString";
URLConnection con = url.openConnection();
con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
con.setDoOutput(true);
// 여기까지가 서버에 접속하기 위한 기본 설정이다.

위 소스를 보자. url은 URL 객체로, 전송할 목적지 주소를 적어주면 된다.
문자열로 boundary를 초기화하고 있는데, 이 때 사용되는 문자열은 어떠한 것이든 상관 없다.
이 문자열은 넘겨지는 각 인자를 구분하기 위한 구분자이다.
con은 URLConnection의 객체로, url에서 바로 열어줄 수 있다.
Content-Type은 보는바와 같이 multipart/form-data로 정해주면 되고, boundary를 설정해주는데
아까 정해놓은 문자열 boundary를 사용하면 되겠다.
자주색으로 되어있는 곳만 자신의 실정에 맞게 수정하자.


위와 같이 했다면 전송할 준비는 마친 것이다. 아래부터 전송할 인자를 설정한다.

DataOutputStream wr = new DataOutputStream(con.getOutputStream());

wr.writeBytes("\r\n--" + boundary + "\r\n");
wr.writeBytes("Content-Disposition: form-data; name=\"no\"\r\n\r\n" + no);
wr.writeBytes("\r\n--" + boundary + "\r\n");
wr.writeBytes("Content-Disposition: form-data; name=\"table_name\"\r\n\r\n" +tableName);
//여기까지가 문자열을 보내는 소스이다.

위 소스에서 파란색은 서버단에서 받을 때 사용하는 Key다. PHP로 치자면
$_POST["이 곳에 들어갈"]; Key 인 것이다.
한 마디로, 넘어온 자료들을 식별하는 이름이라고 하자.
자주색은 실제 넘길 값이다. 말 그대로 서버로 넘기고 싶은 자료(변수)를 넣어주면 된다.
위에 보면 boundary 앞에 --을 붙였는데, --boundary 가 하나의 인자를 감싸는 형태로 추가된다. 하나의 인자 위 아래에 boundary가 있어서 boundary를 만나는 것으로 인자의 시작과 끝을 알 수 있는 것이다.
만약 boundary를 "0090090"으로 정하였다면
--0090090 이 문자열이 각 인자를 위 아래에서 감싸는 것이다.
위 내용에서 \r\n은 줄바꿈 문자이다.

위의 내용을 해석해보면
(줄바꿈)
--SpecificString
(줄바꿈)
Content-Disposition: form-data; name="no"
(줄바꿈)
(줄바꿈)
16

(줄바꿈)
--SpecificString
(줄바꿈)
Content-Disposition: form-data; name="table_name"
(줄바꿈)
(줄바꿈)
test
...이하 줄임...
이런식으로 될 것이다.



위에서 문자열을 넘기는 법에 대해 알아보았다. 이어서 아래의 소스를 사용하면 이미지도 같이 전송할 수 있다.

wr.writeBytes("\r\n--" + boundary + "\r\n");
wr.writeBytes("Content-Disposition: form-data; name=\"image\"; filename=\"image.jpg\"\r\n");
wr.writeBytes("Content-Type: application/octet-stream\r\n\r\n");
FileInputStream fileInputStream = new FileInputStream(cropedImageUri.getPath());
int bytesAvailable = fileInputStream.available();
int maxBufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];

int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0)
{
// Upload file part(s)
DataOutputStream dataWrite = new DataOutputStream(con.getOutputStream());
dataWrite.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available(); 
bufferSize = Math.min(bytesAvailable, maxBufferSize); 
bytesRead = fileInputStream.read(buffer, 0, bufferSize); 
fileInputStream.close();
파란색 image는 역시 서버에서 사용할 변수명이고, 자주색 image.jpg는 서버 변수에 저장될 값이다.(여기선 파일명)
자주색 cropedImageUri.getPath()는 전송할 파일의 경로이다. 전송하고픈 파일의 절대경로를 넣어주면 된다. file://로 시작한다.
maxBufferSize는 1024로 냅두지만 문제가 생길 경우 임의로 수정한다.
이미지를 전송하는 부분부터 아이폰의 그 것과 비교된다.
본인이 잘못 알고 있는지는 모르겠지만, 아이폰보다 조금 복잡해보인다.
이하 소스는 그대로 사용해도 무방하다.
사실 소스는 이해하고 사용하는 것이 중요한데, 소스 전체를 설명드리지 못하는 점 양해 부탁드린다. 여러분의 검색능력을 본인은 믿는다.

여기까지가 문자와 이미지를 POST로 함께 넘기는 방법이다. 설정이 끝났으니 서버로 전송해보자. 사실 전송은 지금까지 계속 해왔다. wr.write- 메서드를 사용해서 말이다.

wr.writeBytes("\r\n--" + boundary + "--\r\n");
wr.flush();
단 두줄이지만 이 두 줄이 매우 중요하다. 우선 위의 boundary는 이 전의 boundary들과 다르다. 바로 뒤에 --이 붙는다는 것이다. boundary 뒤에 --이 붙음으로써 인자 나열이 끝났음을 알린다.
보면 wr(DataOutputStream)의 writeBytes 메서드를 이용해서 인자를 넘겨주고 있다. 이런식으로 서버에 계속해서 자료를 전송한다.
flush()를 호출함으로써 전송을 마무리한다.

마지막으로 전송이 잘 되었는지 서버에서 피드백하는 자료가 있다면, 그 자료를 받는 방법이다.

BufferedReader rd = null;
rd = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
String line = null;
while ((line = rd.readLine()) != null) {
Log.i("Lifeclue", line);
}
생성했던 URLConnection 객체를 이용해 InputStream()을 가져올 수 있는데 이를 이용하여 서버에서 쏴주는 값을 받아올 수 있다. 서버에서 자료를 잘 받았을 때와 잘 받지 못했을 때를 구분해서 처리한다면 단말단에서는 전송한 자료가 잘 갔는지를 판단할 수 있다.

이렇게 POST방식으로 다른 자료형을 함께 보내는 법에 대해 알아보았다. 가끔은 졸려서, 가끔은 바빠서 포스트를 어려차례 나눠쓰는 바람에 내용이 부실할 지도 모르고 가끔은 오탈자나 문맥 이상등으로 이해할 수 없는 부분이 있을지 모르겠다. 여유시간에 작성하는 것인만큼 양해 부탁드린다.



댓글 1개: