UIKit - Multipart File 업로드 요청 구현하기
UIKit - Multipart File 업로드 요청 구현하기
multipart/form-data란?
multipart/form-data는 서버로 파일이나 대용량 데이터를 전송할 때 사용하는 HTTP Content-Type임.
boundary로 경계선을 그으며, 경계선을 기준으로 나눠진 데이터 조각들을 part라고 함.
multipart/form-data를 사용하면 하나의 요청에서 텍스트 데이터와 파일 모두 전송이 가능함.
HTTP 요청 body 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"
david
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="age"
20
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="profile.png"
Content-Type: image/png
(binary data...)
------WebKitFormBoundary7MA4YWxkTrZu0gW--
boundary를 통해 파트를 구분짓고 있으며, 각 파트에는 요청을 보낼 데이터 정보가 들어가있음.
--boundary로 각 파트의 시작을 알리고 있으며, 모든 파트 기입이 끝났다면 --boundary--로 끝났음을 명시해줘야함.
URLSession을 통해 구현하기
URLSession을 통해 multipart/form-data를 구현하기 위해서는 body를 직접 만들어줘야함.
Alamofire같은 오픈소스를 사용하면 훨씬 수월하게 구현이 가능함.
파일 업로드를 처리할 때는 URLSession으로 API를 호출할 때 일반적으로 사용하는 dataTask가 아닌 uploadTask를 사용할 것임.
uploadTask
uploadTask는 이름에서 알 수 있듯이 파일 전송이나 대용량 데이터 업로드에 특화되어 있음.
백그라운드 세션을 지원하기 때문에 업로드 중 앱을 종료하더라도 업로드 처리가 가능함.
dataTask는 백그라운드 세션을 지원하지 않음.
예시
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
import UIKit
final class FileService{
public func upload(data: Data, completionHandler: @escaping (Result<UploadFileResponse, NetworkingError>) -> Void){
// boundary 값은 요청 내에서 고유해야하기 때문에 UUID를 통해 생성
let uuid = UUID().uuidString
var request = URLRequest(url: url)
request.httpMethod = "POST"
// multipart/form-data 헤더 설정
request.setValue("multipart/form-data; boundary=\(uuid)", forHTTPHeaderField: "Content-Type")
// body 설정
var body = Data()
// CRLF(\r\n)로 헤더와 데이터 구분
body.append("--\(uuid)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"image.jpg\"\r\n".data(using: .utf8)!)
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
body.append(data)
body.append("\r\n".data(using: .utf8)!)
body.append("--\(uuid)--\r\n".data(using: .utf8)!)
// URLSession uploadTask를 통해 파일 업로드 처리
urlSession.uploadTask(with: request, from: body) { data, response, error in
...
}.resume()
}
}
Reference
This post is licensed under CC BY 4.0 by the author.