HttpURLConnection을 사용하여 Multipart Upload를 수행하고 그 과정을 ProgressBar로 보여주기를 하는 코드이다.


다음 순서대로 작성하고 적용하면 끝.





1. 인터페이스 생성



1
2
3
4
public interface ProgressListener { 
   void onProgressUpdate(int progress);
}
 
cs




2. MultipartUpload 클래스



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 
public class MultipartUpload {
 
    private final String boundary;
    private final String tail;
    private static final String LINE_END = "\r\n";
    private static final String TWOHYPEN = "--";
    private HttpURLConnection httpConn;
    private String charset;
    private PrintWriter writer;
    private OutputStream outputStream;
    private static final String TAG = "MultipartUtility";
    int maxBufferSize = 1024;
    private ProgressListener progressListener;
    private long startTime;
 
    public MultipartUpload(String requestURL, String charset) throws IOException {
        this.charset = charset;
 
        boundary = "===" + System.currentTimeMillis() + "===";
        tail = LINE_END + TWOHYPEN + boundary + TWOHYPEN + LINE_END;
        URL url = new URL(requestURL);
        httpConn = (HttpURLConnection) url.openConnection();        
        httpConn.setDoOutput(true); 
        httpConn.setDoInput(true);
        httpConn.setRequestProperty("Content-Type""multipart/form-data; boundary=" + boundary);
    }
 
    public void setProgressListener(ProgressListener progressListener) {
        this.progressListener = progressListener;
    }
 
    public JSONObject upload(HashMap<StringString> params, HashMap<StringString> files) throws IOException {
        String paramsPart = "";
        String fileHeader = "";
        String filePart = "";
        long fileLength = 0;
        startTime = System.currentTimeMillis();
 
        ArrayList<String> paramHeaders = new ArrayList<>();
        for (Map.Entry<StringString> entry : params.entrySet()) {
 
            String param = TWOHYPEN + boundary + LINE_END
                    + "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINE_END
                    + "Content-Type: text/plain; charset=" + charset + LINE_END
                    + LINE_END
                    + entry.getValue() + LINE_END;
            paramsPart += param;
            paramHeaders.add(param);
        }
 
        ArrayList<File> filesAL = new ArrayList<>();
        ArrayList<String> fileHeaders = new ArrayList<>();
 
        for (Map.Entry<StringString> entry : files.entrySet()) {
            
            File file = new File(entry.getValue());
            fileHeader = TWOHYPEN + boundary + LINE_END
                    + "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + file.getName() + "\"" + LINE_END
                    + "Content-Type: " + URLConnection.guessContentTypeFromName(file.getAbsolutePath()) + LINE_END
                    + "Content-Transfer-Encoding: binary" + LINE_END
                    + LINE_END;
            fileLength += file.length() + LINE_END.getBytes(charset).length;
            filePart += fileHeader;
 
            fileHeaders.add(fileHeader);
            filesAL.add(file);
        }
        String partData = paramsPart + filePart;
 
        long requestLength = partData.getBytes(charset).length + fileLength + tail.getBytes(charset).length;
        httpConn.setRequestProperty("Content-length""" + requestLength);
        httpConn.setFixedLengthStreamingMode((int) requestLength);
        httpConn.connect();
 
        outputStream = new BufferedOutputStream(httpConn.getOutputStream());
        writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true);
 
        for (int i = 0; i < paramHeaders.size(); i++) {
            writer.append(paramHeaders.get(i));
            writer.flush();
        }
 
        int totalRead = 0;
        int bytesRead;
        byte buf[] = new byte[maxBufferSize];
        for (int i = 0; i < filesAL.size(); i++) {
            writer.append(fileHeaders.get(i));
            writer.flush();
            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filesAL.get(i)));
            while ((bytesRead = bufferedInputStream.read(buf)) != -1) {
 
                outputStream.write(buf, 0, bytesRead);
                writer.flush();
                totalRead += bytesRead;
                if (progressListener != null) {
                    float progress = (totalRead / (float) requestLength) * 100;
                    progressListener.onProgressUpdate((int) progress);
                }
            }
            outputStream.write(LINE_END.getBytes());
            outputStream.flush();
            bufferedInputStream.close();
        }
        writer.append(tail);
        writer.flush();
        writer.close();
 
        JSONObject jObj = null;
        StringBuilder sb = new StringBuilder();
        // checks server's status code first
        int status = httpConn.getResponseCode();
        if (status == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"), 8);
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            reader.close();
            httpConn.disconnect();            
        } else {
            throw new IOException("Server returned non-OK status: " + status + " " + httpConn.getResponseMessage());
        }
        try {
            jObj = new JSONObject(sb.toString());
        } catch (JSONException | NullPointerException e) {
            e.printStackTrace();
        }
        return jObj;
 
    }
 
}
cs





3. 사용법



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class UploadAsync extends AsyncTask<Object, Integer, JSONObject> implements ProgressListener {
 
    private ProgressDialog progressDialog;
    private Context mContext;    
    private HashMap<StringString> param;
    private HashMap<StringString> files;
 
    public UploadAsync(Context context, HashMap<StringString> param, HashMap<StringString> files) {
        mContext = context;
        this.param = param;
        this.files = files;
    }
 
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        startTime = System.currentTimeMillis();
        progressDialog = new ProgressDialog(mContext);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setMessage(mContext.getString(R.string.wait));
        progressDialog.setMax(100);
        progressDialog.setCancelable(false);
        progressDialog.show();
    }
 
    @Override
    protected JSONObject doInBackground(Object... params) {
        JSONObject json = null;
        try {
        
            String url = "http://요청할 URL";
            MultipartUpload multipartUpload = new MultipartUpload(url, "UTF-8");
            multipartUpload.setProgressListener(this);
            json = multipartUpload.upload(param, files);            
 
        } catch (IOException e) {
            e.printStackTrace();
        }
        return json;
 
    }
 
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        if (progressDialog != null && progressDialog.isShowing()) {
            if (values[1== 1) {
                progressDialog.setProgress(values[0]);
            } else {
                progressDialog.setProgress(values[0]);
            }
        }
    }
 
    @Override
    protected void onPostExecute(JSONObject result) {
        super.onPostExecute(result);
        if (progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
 
        if (result != null) {
            try {
                if (result.getInt("success"== 1) {                            
                    Toast.makeText(mContext, R.string.success, Toast.LENGTH_SHORT).show();
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        } else {
            Toast.makeText(mContext, R.string.conntection_error, Toast.LENGTH_SHORT).show();
        }
    }
 
    @Override
    public void onProgressUpdate(int progress) {
        publishProgress(progress, 0);
    }
}
 
cs


  네트워크 작업이므로 반드시 스레드 안에서 실행해야 한다.


  생성자로 접속할 url과 캐릭터셋을 넣고,  프로그레스를 받을 인터페이스 리스너를 등록 한 후 파라미터 파트와 파일 파트를 HashMap으로 구성하여 인자로 넘겨준다. 


  한가지 문제점은 실제로 접속하여 파일을 전송하는 과정이 progress되지 않는다는 점이다. MultipartUpload 클래스에 보면 outputstream에 쓰는 과정을 progress로 보여주는데 사실 이것이 파일이 전송됨을 의미하지는 않는다는 점이다. 단지 버퍼에서 읽는 부분일뿐.. 실제 예제를 만들어 progress가 100%가 후에 전송을 시작하여 마치 앱이 멈춘것 처럼 동작한다. 파일 크기가 작으면 전송이 빨리되어 약간의 딜레이가 생기겠지만 파일이 큰 경우 오류난것 처럼 보인다. 


  버퍼에 쓴다음 바로 flush를 해줘도 똑같다. setFixedLenghStreamingMode를 해주면 된다고는 하는데 안된다! 이거 실제 전송되는 부분 아시는분은 알려주시면 감사하겠습니다. 


+ Recent posts