안드로이드 서버에 이미지 업로드 꼭 답변 부탁드려요!

조회수 812회

안녕하세요. 모 앱 개발 중 API 레벨을 30으로 업데이트가 필요하여 진행 중 다음과 같은 이슈가 발생하였습니다.

API 레벨 30으로 업데이트 하면서 WRITE_EXTERNAL_STORAGE은 하위 버전에서만 작동하게 했습니다. requestLegacyExternalStorage도 물론 제거하였습니다.

https://developer.android.com/about/versions/11/privacy/storage#permissions-target-11

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28"/>

문제는 앱 내에서 서버로 이미지 파일을 업로드 하는 구간에서 발생하였습니다. uCrop(사진 편집) 라이브러리를 이용하여 유저가 사진을 크롭하면 그 결과물의 uri를 받아 파일로 변환 이후 HttpURLConnection API를 이용하여 사진 업로드를 하였으나 WRITE_EXTERNAL_STORAGE 권한이 더이상 작동하지 않아 서버에 업로드가 되지 않습니다.(Retrofit2은 익숙치 않고 주어진 시간이 촉박하여 이용하지 않았습니다.)

오류 로그의 내용은 "FileNotFoundException EACCES (Permission denied)" 인데 쓰기 권한이 없는 상태에서 파일을 생성하려 하니 당연히 나오는 에러이지만 위에도 적었듯이 WRITE_EXTERNAL_STORAGE 권한은 더이상 먹히지 않는데 어떻게 수정을 해줘야 할지 모르겠습니다.

// onActivityResult
case UCrop.REQUEST_CROP: {
    if (resultCode == RESULT_OK) {
        Uri resultUri = UCrop.getOutput(data);
        if (resultUri == null)
            return;
        try {
            String md5sum = MatrixUtil.getMD5fromFile(resultUri.getPath());
            String uidKey = MatrixUtil.urlEncode(myApplication.readMemberUid());

            dialog = new ProgressDialog(ProfileModifyActivity.this);
            dialog.setMessage("잠시만 기다려주세요.");
            dialog.setIndeterminate(true);
            dialog.setCancelable(true);
            dialog.show();

            String params = String.format("%s?act=member_photo&uid=%s&photo=%s", WebProtocol.getMemberPhotoUrl(getApplicationContext()), uidKey, md5sum);
            httpManager.uploadFile(params, resultUri.getPath(), "", ProfileModifyActivity.this, WebProtocol.REQUEST_CODE_MEMBER_PHOTO_UPLOAD);
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else if (resultCode == UCrop.RESULT_ERROR) {
        final Throwable throwable = UCrop.getError(data);
        Toast.makeText(ProfileModifyActivity.this, String.valueOf(throwable), Toast.LENGTH_SHORT).show();
        Log.d("UCrop_Error", String.valueOf(throwable));
    }
}
// 기존 버전(API 29)에서 사용된 이미지 파일 서버 업로드 코드
public void uploadFile(final String urlServer, final String filePath, final String params, final IHttpUpload listener, final int type) {
    Thread networkThread = new Thread(new Runnable() {
    @Override
    public void run() {
        File file = new File(filePath);
        // ....
        try {
            conn = (HttpURLConnection) ((new URL(urlServer).openConnection()));
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("ENCTYPE", "multipart/form-data");
            conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

            if (filePath.length() > 0) {
                conn.setRequestProperty("photo", file.getName());
                // 디버깅 시 FileInputStream, DataOutputStream에서 
`               // FileNotFoundException 오류가 발생합니다.
                FileInputStream in = new FileInputStream(filePath);
                DataOutputStream out = new DataOutputStream(new BufferedOutputStream(conn.getOutputStream()));
                out.writeBytes(twoHyphens + boundary + lineEnd);
                out.writeBytes("Content-Disposition: form-data; name=\"photo\";filename=\"" + file.getName() + "\"" + lineEnd);
                out.writeBytes(lineEnd);

                int maxBufferSize = 4096 * 2;
                int bufferSize = Math.min(in.available(), maxBufferSize);
                byte[] buffer = new byte[bufferSize];
                int byteRead = in.read(buffer, 0, bufferSize);
                // ...
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null)
                conn.disconnect();
            }
        }
     }
});
networkThread.start();
}

질문을 요약하자면,

HttpURLConnection 또는 Retrofit2을 이용하여 서버에 이미지를 업로드 하는 방법을 알고 싶습니다.

다만 API 30에 대응이 되는(WRITE_EXTERNAL_STORAGE 권한이 필요없는) 방법을 꼭 알고 싶습니다. 해당 문제로 너무 오래 고민을 하다가 질문 올려봅니다 꼭 부탁드려요!

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)