[Android] 서버에 이미지 업로드하기 03
앞서 서버에 이미지 업로드하기 과정에서 발생했던 문제 해결 및 수정
+) 서버에 이미지 업로드하기 02 참고
[STUDY/Android] 서버에 이미지 업로드하기02
PHP를 이용한 이미지 업로드가 잘 해결되지 않아 새로운 방식으로 도전해봤다. JSP를 이용하는 방법이다. 우선, JSP를 이용하기 위해서 eclipse, tomcat, MySQL 총 3가지를 설치해주었다. 설치 방법은 아
baehj.tistory.com
- 서버 실행 문제
이전 시도에서 톰캣 폴더에 이미지를 직접 넣어봐도 서버에서 이미지가 뜨지 않았는데, 그때 서버가 계속 실행되고있는 상태가 아니어서 발생하는 설치 문제인것 같다는 의견을 듣고 아예 프로그램을 삭제하고 다시 다운받아 보았다.
프로그램 재설치결과, 다행히 다운 과정에서 문제가 있었던건지 정상적으로 작동하게 되었다.
-> 톰캣 폴더에 이미지를 직접 넣고, 서버에서 해당 경로를 열었을때 이미지가 정상적으로 출력되는것을 볼 수 있었다.
- eclipse에서 jsp 파일 작성시, 여러 오류 발생
jsp 코드에는 문제가 전혀 없는 상태에서 계속해서 오류가 발생했었다.
검색을 통해 프로젝트와 시스템의 jdk 버전 차이 문제일 수 있다는 내용을 확인하고 수정해주었다.
수정 후, jsp 파일의 오류 대부분이 해결됐고, 예외처리와 Enumeration 등에서 발생한 오류도 해결했다.
JspImage 프로젝트
- MainActivity.java
package com.example.app2;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.StrictMode;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
ImageView imageView = null;
Button button = null;
private final int REQ_CODE_SELECT_IMAGE = 100;
private String img_path = new String();
private String serverURL = "http://localhost/ImageProject/test.jsp";
private Bitmap image_bitmap_copy = null;
private Bitmap image_bitmap = null;
private String imageName = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.permitDiskReads()
.permitDiskWrites()
.permitNetwork().build());
imageView = (ImageView) findViewById(R.id.imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, REQ_CODE_SELECT_IMAGE);
}
});
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DoFileUpload(serverURL, img_path);
Toast.makeText(getApplicationContext(), "이미지 전송 성공", Toast.LENGTH_SHORT).show();
Log.d("app2", "Success");
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
Toast.makeText(getBaseContext(), "resultCode : " + data, Toast.LENGTH_LONG).show();
if (requestCode == REQ_CODE_SELECT_IMAGE) {
if (resultCode == Activity.RESULT_OK) {
try {
img_path = getImagePathToUri(data.getData());
Toast.makeText(getBaseContext(), "img_path : " + img_path, Toast.LENGTH_SHORT).show();
image_bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
int reWidth = (int) (getWindowManager().getDefaultDisplay().getWidth());
int reHeight = (int) (getWindowManager().getDefaultDisplay().getHeight());
image_bitmap_copy = Bitmap.createScaledBitmap(image_bitmap, 400, 300, true);
ImageView image = (ImageView) findViewById(R.id.imageView);
image.setImageBitmap(image_bitmap_copy);
} catch (Exception e) {
e.printStackTrace();
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public String getImagePathToUri(Uri data) {
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(data, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String imgPath = cursor.getString(column_index);
Log.d("app2", imgPath);
String imgName = imgPath.substring(imgPath.lastIndexOf("/") + 1);
Toast.makeText(MainActivity.this, "이미지 이름 : " + imgName, Toast.LENGTH_SHORT).show();
this.imageName = imgName;
return imgPath;
}
public void DoFileUpload(String apiUrl, String absolutePath) {
HttpFileUpload(apiUrl, "", absolutePath);
}
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
public void HttpFileUpload(String urlString, String params, String fileName) {
try {
FileInputStream mFileInputStream = new FileInputStream(fileName);
URL connectUrl = new URL(urlString);
Log.d("app2", "mFileInputStream is " + mFileInputStream);
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);
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("app2", "image byte is " + bytesRead);
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);
Log.e("app2", "File is written");
mFileInputStream.close();
dos.flush();
InputStream is = conn.getInputStream();
StringBuffer b = new StringBuffer();
for (int ch = 0; (ch = is.read()) != -1;) {
b.append((char) ch);
}
is.close();
Log.e("app2", b.toString());
} catch (Exception e) {
Log.d("app2", "exception " + e.getMessage());
}
}
}
- test.jsp
<%
String folderTypePath = application.getRealPath("/testdir");
String name = new String();
String fileName = new String();
int sizeLimit = 5 * 1024 * 1024;
try {
MultipartRequest multi = new MultipartRequest(request, folderTypePath, sizeLimit, new DefaultFileRenamePolicy());
Enumeration files = multi.getFileNames();
if (files.hasMoreElements()) {
name = (String) files.nextElement();
fileName = multi.getFilesystemName(name);
}
} catch (Exception e) {
out.println("안드로이드로부터 이미지를 받아옵니다.");
}
%>
Manifest)
internet과 storage를 위한 권한을 설정한다.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />
Mainactivity)
- StrictMode라는 API가 안드로이드 진저브레드부터 탑재되었는데 허니콤 버전부터 이 StrictMode가 기본적으로 허용되게 만들어져있기 때문에 네트워크 요청이나 디스크와 관련된 처리들은 이 스레드를 분리해서 처리해야 하는 상황이 됐다.
이때, 간단하게 StrictMode를 해제하여 네트워크 처리를 해당 스레드에서도 처리할 수 있게 해줄 수 있는 방법이 있다.
아래 코드가 그 방법이다.
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.permitDiskReads()
.permitDiskWrites()
.permitNetwork().build());
- onActivityResult 함수를 통해 Intent로 갤러리에서 이미지를 선택하고 불러올 수 있도록 해준다.
여기서 이미지의 Uri를 얻어서 경로값으로 반환해 비트맵형식으로 얻고 사이즈를 조절해서 이미지뷰에 해당 이미지를 띄운다.
- 위의 onActivityResult 함수에서 선택한 이미지의 uri 정보를 매개로 해당 이미지의 정보를 Cursor를 통해 반환한다.
사용자가 선택한 이미지 정보를 가져오고 이미지의 경로값과 이름값을 만들어 DoFileUpload 함수를 통해 이미지를 서버로 전송한다.
- DoFileUpload 함수는 HttpFileUpload 함수로 이루어져있는데, 이 HttpFileUpload 함수는 서버에 선택한 이미지를 전송하기 위한 이미지의 Uri 정보를 매개로 byte 단위로 서버에 전달하는 함수이다.
test.jsp)
- 이미지를 저장할 경로를 입력할때 내 컴퓨터의 폴더에 저장하고자 했을 경우 경로를 "/Users/baehyunjin/Desktop/testdir" 이라고 입력해주면 됐지만, 서버에서 바로 이미지를 확인해볼 수 있도록 하기 위해서 경로를 application.getRealPath("/testdir")로 변경했다.
내 컴퓨터 폴더에 저장하고자 할 경우, 직접 폴더를 미리 원하는 경로에 만들어둬야한다.
내가 바꾼 방식으로 할 경우에는 tomcat 폴더에서 webapp 폴더 속 해당하는 프로젝트 폴더 안에 원하는 폴더를 생성해줘야한다.
문제발생
- open failed: EACCES (Permission denied) 오류 발생.
-> external 저장 공간의 파일에 대해서 read 또는 write 하기위한 별도의 권한이 필요하기 때문에 발생한다.
때문에 AndroidManifest.xml 파일에 해당 권한을 추가해주면 되는 것이지만, 나는 앞서서 이미 권한을 부여한 상태였다.
그런데도 오류가 발생하고 있었고, 이유를 찾아봤더니 권한을 주었어도 애뮬레이터 실행 시, 따로 기기의 설정에서 해당 app의
permission에 대한 설정을 해줘야했다. 그대로 해주니 오류없이 잘 실행됐다.
- exception Cleartext HTTP traffic to ~ not permitted 오류 발생.
-> 안드로이드에서 기본적으로 Http 접근을 허용하지 않기 때문에 발생하는 오류이다.
안드로이드 파이부터 cleartext HTTP를 비활성화하기 때문에 그 이후부터 Http에 접근하려면 따로 활성화시켜줘야한다.
<application ~ > 안에 아래 코드를 넣어서 모든 Http 주소에 접근할 수 있도록 만들어준다.
android:requestLegacyExternalStorage="true"
실행결과
안드로이드 스튜디오로 tomcat 서버에 jsp 로 이미지 업로드하기 드디어 성공하였다..!