파일 및 이미지 전송 Android 에서 JSP Tomcat6 on uBuntu Server
Multipar
t는 이미지 전송에 특화된 것이다.
requset
개념과 httpPost
객체 개념이 혼합된 것이다.
1. 안드로이드
MultipartEntity
라는 객체를 이용하게 된다. 이 객체는 파일 형식으로 데이터를 보내게 된다.
multipart를 사용하면 쉽게 문자열과 파일들을 묶어서 한번에 전송 할 수 있다.
하지만 안드로이드에서 기본적으로 지원하는 HttpClient
에서는 Multipart
가 지원되지 않는다.
따라서 Multipart
를 사용하기 위해서는 Apache HttpComponents
에서 받아야 한다.
하지만 최신 버전 4.5.*
는 안드로이드 에서 동작하지 않는다.
필자는 4.3.1을 사용 한다.
httpclient
, httpcore
, httpmine
이 세개가 핵심이다.
해당 코드를 사용해서 구현한 App은 NetworkUsage
이다.
하지만, Android 2.3.3
에서는 정상적으로 동작 하지만, 5.0
에서는 오류가 발생 한다.
코드분석
String sdcard = Environment.getExternalStorageDirectory().getPath();
// 파일을 서버로 보내는 부분
try {
HttpClient client = new DefaultHttpClient(); // httpClient 생성
String url = "서버주소/실행.jsp";
HttpPost post = new HttpPost(url); // httpPost 생성
// FileBody 객체를 이용해서 파일을 받아옴
File glee = new File(sdcard+"/glee.jpg"); // file 객체 생성
FileBody bin = new FileBody(glee); // FileBody 생성
MultipartEntity multipart = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
multipart.addPart("images", bin); //실제 파일을 multipart에 넣는다.
post.setEntity(multipart); // Multipart를 post 형식에 담음
client.execute(post); // post 형식의 데이터를 서버로 전달
Deplicate된 것을 사용하지 않고 Builder
를 이용하는 방법
4.3
이상의 httpmine을 사용할경우 Builder
를 사용하라고 지시하게 되어 있다.
@Override
protected String doInBackground(String... params) {
String sdcard = Environment.getExternalStorageDirectory().getPath();
// 파일을 서버로 보내는 부분
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
File glee = new File(sdcard+"/my.db");
FileBody bin = new FileBody(glee);
MultipartEntityBuilder meb = MultipartEntityBuilder.create();
meb.setCharset(Charset.forName("UTF-8"));
meb.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
meb.addPart("database", bin);
HttpEntity entity = meb.build();
post.setEntity(entity);
try {
HttpResponse reponse = client.execute(post);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // post 형식의 데이터를 서버로 전달
return "SUCCESS";
}
2. Tomcat6 Server (JSP)
클라이언트에서 파일을 서버에 업로드 하기위한 <form>
태그는 아래와 같은 형식을 따라야 한다.
<form action="???.jsp" method="post" enctype="multipart/form-data">
method는 반드시 POST 방식
전송할 데이터의 인코딩 방식은 multipart/form-data
GET 방식의 경우 URL에 데이터를 삽입하는 방식으로 255개의 문자만 전송이 가능하므로 파일 전송에는 적합하지 않다.
enctype의 경우에도 Default 값은 urlencoded
방식을 사용하게 된다. 하지만 파일의 경우 ASCII파일 뿐만 아니라 이진 파일의 내용도 전송해야 하므로 multipart/form-data
방식으로 인코딩 하고 <input>
태그에서 type 속성을 file
로 지정해서 전송 해야 한다.
파일 업로드 : <input type="file" name="upload"><br>
<input type="submit" value="업로드">
파일업로드 구현시에는 아래의 방법을 많이 사용한다. 가장 쉬운 방법이기 때문이다.
JSP에서 MultipartRequest
를 사용하기 위해서는
com.oreilly.servlet
을 import 해야 한다.
Download Link
필자는 cos-26Dec2008.zip
를 다운 받았다.
설치방법
/var/lib/tomcat6/webapps/ROOT/AppRating
/WebContent/WEB-INF/lib
cos-26Dec2008.zip
압축 해제후 cos.jar 파일을 lib
위치로 이동 시킨다.
소스코드
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<%@page
import="com.oreilly.servlet.MultipartRequest,com.oreilly.servlet.multipart.DefaultFileRenamePolicy,java.util.*,java.io.*"%>
<%
//주의사항. 파일저장 정확한 경로아래에 폴더가 만들어져 있어야한다.
//폼에서 넘길때 enctype="multipart/form-data"
//정확한경로의 설정및 확인방법은 다음과같으며...
String realFolder = ""; //파일경로를 알아보기위한 임시변수를 하나 만들고,
String saveFolder = "filestorage"; //파일저장 폴더명을 설정한 뒤에...
String encType = "euc-kr"; //인코딩방식도 함께 설정한 뒤,
int maxSize = 100 * 1024 * 1024; //파일 최대용량까지 지정해주자.(현재 100메가)
ServletContext context = getServletContext();
realFolder = context.getRealPath(saveFolder);
System.out.println("the realpath is : " + realFolder); // file path
File dir = new File(realFolder); // 디렉토리 위치 지정
if (!dir.exists()) { // 디렉토리가 존재하지 않으면
dir.mkdirs(); // 디렉토리 생성.!
}
// print current time
Date today = new Date();
System.out.println(today);
try {
//멀티파트생성과 동시에 파일은 저장이 되고...
MultipartRequest multi = null;
multi = new MultipartRequest(request, realFolder, maxSize,
encType, new DefaultFileRenamePolicy());
//이 시점을기해 파일은 이미 저장이 되었다.
//폼에서 넘어왔던파일 파라메터들을 가져오려면 이렇게.
Enumeration params = multi.getParameterNames();
//그리고 가져온 파라메터를 꺼내는 방법...
while (params.hasMoreElements()) {
String name = (String) params.nextElement();//파라메터이름을 가져온뒤
String value = multi.getParameter(name);//이름을 이용해 값을가져온다
System.out.println(name + " = " + value);
application.log(name + " = " + value); // logManager
}
//이번엔 파일과 관련된 파라메터를 가져온다.
Enumeration files = multi.getFileNames();
//이번엔 파일관련 파라메터를 꺼내본다...
while (files.hasMoreElements()) {
String name = (String) files.nextElement();//파라메터이름을 가져온뒤
String filename = multi.getFilesystemName(name);//이름을 이용해 저장된 파일이름을 가져온다.
String original = multi.getOriginalFileName(name);//이름을이용해 본래 파일이름도 가져온다.
String type = multi.getContentType(name);//파일타입도 가져올수있다.
File f = multi.getFile(name);//파일 용량을 알아보기위해서는 이렇게...
System.out.println("Parameter Name: " + name);
System.out.println("Real File Name: " + original);
System.out.println("Saved File Name: " + filename);
System.out.println("File Type: " + type);
if (f != null) {
System.out.println("File Size: " + f.length());
}
System.out.println("-------------------------------");
}
} catch (IOException ioe) {
System.out.println(ioe);
} catch (Exception ex) {
System.out.println(ex);
}
%>
결론
Apache HTTPClient
방법은 Android
에서 정식으로 지원하는 방법이 아니므로 버전과 환경에 너무 민감하다.
권장하고있는 HttpURLConnection
방법으로 구현하는게 맞는것 같다.
하지만 Stream
처리를 해야하므로 아무래도 좀더 코드양도 많고 귀찮기는 하다.
참고사이트
http://derveljunit.tistory.com/5
http://derveljunit.tistory.com/entry/JSP%EC%97%90%EC%84%9C-DB-%EC%97%B0%EA%B2%B0-%EB%B0%A9%EB%B2%95
http://blog.hansune.com/289
http://yoongi.tistory.com/87