본문 바로가기

Develop/Android SDK

100% 안드로이드에서 이미지 파일 서버로 업로드하기 (소스 공개)


아~ 간만에 고생좀 했당.. 하기 싫은거 할려니ㅋㅋ
서버 구성은 이렇다..
톰캣5.5 + 스트럿츠

지인의 조언.. 그 많은 구글링 예제 소스.. 다 필요 없음(과장해서..ㅎㅎ 지인분들 감사합니다..^^;;).. 한방에 해결..!!

스트럿츠는 FormFile 타입으로 쉽에 업로드 할수 있기에 안드로이드에서 Formfile 타입으로 보내보려고 했었지만.. 실패ㅠ

먼저 안드로이드쪽부터 살펴보면..
갤러리에서 이미지 선택한다음
  -> onActivityResult(int requestCode, int resultCode, Intent intent) 에서 받아서 아래와 같이 처리!

 
Uri selPhotoUri = intent.getData();

 //나중에 이미지뷰에 뿌려주기 위해 담아놓음.
Bitmap selPhoto = Images.Media.getBitmap( getContentResolver(), selPhotoUri );  
Log.e("전송","시~~작 ~~~~~!");

String urlString = "서버에 전송할 API URL을 넣는다.";

//절대경로를 획득한다!!! 중요~
Cursor c = getContentResolver().query(Uri.parse(selPhotoUri.toString()), null,null,null,null);
c.moveToNext();
String absolutePath = c.getString(c.getColumnIndex(MediaStore.MediaColumns.DATA));

//파일 업로드 시작!
DoFileUpload(urlString , absolutePath);


그럼 이제 DoFileUpload 메소드를 보자.
 public void DoFileUpload(String apiUrl, String absolutePath) {
  HttpFileUpload(apiUrl, "", absolutePath); 
 }


다음 HttpFileUpload 메소드를 봐야겟져..? 사실 안에 코드 내용은 중요 하지 않아요~ 카피 & 붙여넣기 하세요^-^v
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****"; 

 public void HttpFileUpload(String urlString, String params, String fileName) {
  try {
   
   mFileInputStream = new FileInputStream(fileName);   
   connectUrl = new URL(urlString);
   Log.d("Test", "mFileInputStream  is " + mFileInputStream);
   
   // open connection
   HttpURLConnection conn = (HttpURLConnection)connectUrl.openConnection();   
   conn.setDoInput(true);
   conn.setDoOutput(true);
   conn.setUseCaches(false);
   conn.setRequestMethod("POST");
   conn.setRequestProperty("Connection", "Keep-Alive");
   conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
   
   // write data
   DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
   dos.writeBytes(twoHyphens + boundary + lineEnd);
   dos.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + fileName+"\"" + lineEnd);
   dos.writeBytes(lineEnd);
   
   int bytesAvailable = mFileInputStream.available();
   int maxBufferSize = 1024;
   int bufferSize = Math.min(bytesAvailable, maxBufferSize);
   
   byte[] buffer = new byte[bufferSize];
   int bytesRead = mFileInputStream.read(buffer, 0, bufferSize);
   
   Log.d("Test", "image byte is " + bytesRead);
   
   // read image
   while (bytesRead > 0) {
    dos.write(buffer, 0, bufferSize);
    bytesAvailable = mFileInputStream.available();
    bufferSize = Math.min(bytesAvailable, maxBufferSize);
    bytesRead = mFileInputStream.read(buffer, 0, bufferSize);
   } 
   
   dos.writeBytes(lineEnd);
   dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
   
   // close streams
   Log.e("Test" , "File is written");
   mFileInputStream.close();
   dos.flush(); // finish upload...   
   
   // get response
   int ch;
   InputStream is = conn.getInputStream();
   StringBuffer b =new StringBuffer();
   while( ( ch = is.read() ) != -1 ){
    b.append( (char)ch );
   }
   String s=b.toString();
   Log.e("Test", "result = " + s);
   mEdityEntry.setText(s);
   dos.close();   
   
  } catch (Exception e) {
   Log.d("Test", "exception " + e.getMessage());
   // TODO: handle exception
  }  
 }

이렇게 하면 안드로이드 쪽은 끝~~!! 쉽죠.. 자꾸 이상한 에러들이 나서 난감했음..ㅋ
예를 들면 뭐 스트럿츠에서 request를 두번 받는다느니..서블릿이 포함되서 간다거나, 스트림을 잃는 다거나.. 이런내용이 많았는데.. 에러 상황은 똑같지만 전 그냥 코드를 잘못 입력한듯..ㅋㅋ

자.. 이제 서버쪽..MultipartRequest 는 자주 이용해보셨을껍니다..간단해요.. lib폴더에 cos.jar 넣어 주시고..

import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

소스 안에서는
String folderTypePath = "받는 이미지를 넣을 경로";
int sizeLimit = 5 * 1024 * 1024 ; // 5메가까지 제한 넘어서면 예외발생
MultipartRequest multi = new MultipartRequest(request, folderTypePath, sizeLimit, new DefaultFileRenamePolicy());
Enumeration files = multi.getFileNames();

//파일 정보가 있다면
if(files.hasMoreElements()) {
     name = (String)files.nextElement();
     fileName = multi.getFilesystemName(name);
}
System.out.println("★★★★★★★★★★★★★★★★★ 이미지 업로드 완료 파일명은? : " + fileName);
jsp든.. java든 똑같습니다~
감사합니다;)

by. xranma




2012.09.28 Seriane 님 추가 내용입니다. 정말 감사드립니다^^
안드로이드 3.0이후부터는 onCreate함수 안에 아래 라인을 추가해야 하네요
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads() 
.detectDiskWrites()
.detectNetwork() 
.penaltyLog().build()); 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

참고 사이트
http://android-developers.blogspot.kr/2010/12/new-gingerbread-api-strictmode.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+blogspot/hsDu+(Android+Developers+Blog)


요즘 android에 손댈 일이 없어 테스트는 아직 못해보았습니다ㅠㅠ


2012.10.14 iBluemind님 추가 내용입니다^^ 감사합니다(_ _)

StrictMode라는 API가 안드로이드 진저브레드에서부터 탑재되기 시작했습니다. 그리고, 안드로이드 허니콤부터는 이 StrictMode가 기본적으로 항상 Enable 되게 되었기 때문에, 네트워크 요청이나 디스크 IO와 관련된 처리들은 무조건 UI 스레드와 분리해서 처리해야 하게 되었습니다.

하지만, '꼼수'로 이 StrictMode를 해제하여 네트워크 처리를 UI 스레드에서도 처리할 수 있게끔 할 수 있는 것입니다. 그런데, Seriane님의 댓글에 소개된 코드는 오히려 StrictMode를 Enable하는 코드입니다. 링크해주신 페이지를 보더라도 진저브레드에서 이를 켜는 것이라고 소개되어 있지요.

그래서, 네트워크 처리와 디스크 IO 처리 부분에 대한 StrictMode를 Disable하는 방법은 다음과 같습니다.

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.permitDiskReads() 
.permitDiskWrites()
.permitNetwork().build());

2015.12.13 하.. 정말 오랜만에 작업할려니까.. 하나도 모르겠넹 @_@

여러분~~ 위에 HttpFileUpload 부분을 

private class 클래스명 extends AsyncTask<Void, Void, Void> {

이렇게 쓰레드로 돌리세요~