Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 리펙토링
- MVVM
- uiscrollview
- Protocol
- clean architecture
- uitableview
- collectionview
- HIG
- Clean Code
- tableView
- 리팩토링
- map
- Human interface guide
- swift documentation
- ribs
- SWIFT
- RxCocoa
- Observable
- ios
- UICollectionView
- Refactoring
- 애니메이션
- Xcode
- 클린 코드
- 스위프트
- 리펙터링
- rxswift
- swiftUI
- combine
- UITextView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. Multipart, 멀티파트 - URLSession으로 구현 (uploadTask, URLSessionUploadTask, progress 처리) 본문
iOS 응용 (swift)
[iOS - swift] 2. Multipart, 멀티파트 - URLSession으로 구현 (uploadTask, URLSessionUploadTask, progress 처리)
jake-kim 2023. 7. 1. 01:411. Multipart, 멀티파트 - 개념 (메모리 효율성, 네트워크 대역폭, 개별적 재전송 유리)
2. Multipart, 멀티파트 - URLSession으로 구현 (uploadTask, URLSessionUploadTask, progress 처리)
3. Multipart, 멀티파트 - BackgroundSession 사용 방법 (background에서 업로드, suspended에서 업로드, not running에서 업로드)
Multipart 방식
- 이전 포스팅 글에서 알아보았듯이, Multipart 방식은 대용량 파일을 업로드하기에 좋은 방식
- 파일의 크기에 영향을 받지 않는 장점이 존재
- 메모리 효율성 - 전체 파일을 한꺼번에 메모리에 올리지 않고 쪼개어서 전송
- 네트워크 대역폭에 효율
- 개별적으로 재전송이 쉬움
Multipart방식 구현 전, URLSessionUplaodTask 알아보기
- URLSessionUploadTask를 사용하여 Multipart 방식 사용
- URLSessionUploadTask은 URLSessionTask의 서브클래스
- 일반적인 data task와는 다르게 background에서 파일들을 업로드 할 수 있는 Tsak 방식
cf) URLSessionDataTask란?
- URLSessionTask의 서브클래스
- 데이터를 가져오는 작업을 나타내는 클래스
- 일반적으로 HTTP GET 요청을 사용하여 서버에서 데이터를 가져오는 작업에 사용
- 데이터를 서버로부터 받아오는 작업을 수행하며, 서버 응답 데이터는 메모리에 저장
1) Multipart 구현 - 함수 선언
- 이미지 데이터를 넘겨주고, 네트워킹을 통해 얻어온 결과값을 completion으로 전달
func uploadImageUsingURLSession(imageData: Data, completion: @escaping (Error?) -> Void) {
}
2) Multipart 구현 - url과 HTTP 방식 작성
- URLRequest에 url, HTTP 방식 정보 입력
let url = URL(string: "https://example.com/upload")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
3) Multipart 구현 - 헤더 작성
- 이전 포스팅 글에서 알아본 대로 multipart 방식은 body안에 구분자를 넣어서 form-data들을 구분해야 하므로 boundary라는 값을 content-type (mime-type)으로 작성
- 필요한 두 개의 정보
- multipart/form-data
- boundary
let uniqString = UUID().uuidString
let contentType = "multipart/form-data; boundary=\(uniqString)"
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
4) Multipart 구현 - form-data 작성
- body부분을 작성하기 위해 빈 Data() 인스턴스 준비
var body = Data()
- multipart 방식은 body부분에 구분자를 넣어서 여러개의 data를 넣을 수 있고 특정한 형식이 있으므로 구분자를 작성
// 멀티 파트 데이터의 구분자(boundary) 설정
body.append("--\(uniqString)\r\n".data(using: .utf8)!)
// TODO...
body.append("--\(uniqString)--\r\n".data(using: .utf8)!)
- 위 TODO 부분에 multipart 방식에 사용되는 형태인 Content-Disposition, name, filename, Contnet-Type 등을 작성
body.append("Content-Disposition: form-data; name=\"image\"; filename=\"image.jpg\"\r\n".data(using: .utf8)!)
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
- 실제 이미지 데이터를 추가하고 해당 폼 데이터의 마지막이라는 구분자도 추가하면 body 형태 완료
body.append(imageData)
body.append("\r\n".data(using: .utf8)!)
(body를 채우는 전체 코드 )
/// 폼 데이터 생성
var body = Data()
/// 멀티 파트 데이터의 구분자(boundary)
body.append("--\(uniqString)\r\n".data(using: .utf8)!)
/// 멀티 파트 데이터의 파트에 대한 헤더를 추가: name 파라미터를 "image"로 설정하고, 업로드된 파일의 원래 이름을 "image.jpg"로 설정
body.append("Content-Disposition: form-data; name=\"image\"; filename=\"image.jpg\"\r\n".data(using: .utf8)!)
/// 업로드된 파일의 MIME 타입을 명시
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
/// 실제 이미지 바이너리 데이터를 담고있는 값 추가
body.append(imageData)
/// 각 파트 사이에 빈 줄을 추가하여 파트를 구분하며 멀티파트 데이터의 각 파트를 구분하는 구분자 역할
body.append("\r\n".data(using: .utf8)!)
/// 멀티파트 데이터의 끝을 나타내는 경계값을 추가하며 멀티파트 데이터의 마지막을 나타내고 파트들을 닫는 역할
body.append("--\(uniqString)--\r\n".data(using: .utf8)!)
5) URLSession 만들어서 네트워킹
- 파일 업로드는 URLSession.shared와는 다른 timeout 정책을 가져가서 언제까지 업로드 할건지 타임아웃을 정하는것이 좋으므로 별도의 URLSession을 만들어서 사용
let session = URLSession(configuration: .default)
session.configuration.timeoutIntervalForRequest = TimeInterval(20)
session.configuration.timeoutIntervalForResource = TimeInterval(20)
let task = session.uploadTask(with: request, from: body) { (data, response, error) in
DispatchQueue.main.async {
if let error = error {
completion(error)
return
}
// 응답 처리
completion(nil)
}
}
task.resume()
- task를 바로 resume()하기 전에 delegate를 지정하여 파일들의 업로드 progress 파악도 가능
...
}
task.delegate = self // <- 추가
task.resume()
extension ViewController: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
print("progress = ", Double(bytesSent) / Double(totalBytesSent))
}
}
보너스) 파일들 전송에 개별적인 progress가 필요한 경우 구현 방법
- multipart를 사용하면 여러개의 파일들을 동시에 업로드할때 사용하기 때문에 이때는 각각의 이미지를 upload하도록 구현 필요
- ImageUploader라는 별도의 클래스를 만들고 이 안에서 URLSessionTask를 배열로 관리하도록 설정
- task를 기록해놓는 이유
- 각 task마다 progress를 URLSessionTaskDelegate로 알 수 있기 때문에 이를 활용하기가 용이
import Foundation
import UIKit
class ImageUploader: NSObject {
private var session: URLSession!
private var uploadTasks: [URLSessionTask] = []
func uploadImages(images: [UIImage]) {
let configuration = URLSessionConfiguration.default
session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
for image in images {
guard let imageData = image.jpegData(compressionQuality: 0.8) else {
print("Failed to convert image to data")
continue
}
let url = URL(string: "https://example.com/upload")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let task = session.uploadTask(with: request, from: imageData)
task.delegate = self
uploadTasks.append(task)
task.resume()
}
}
}
extension ImageUploader: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
guard let index = uploadTasks.firstIndex(of: task) else { return }
let progress = Double(totalBytesExpectedToSend) / Double(totalBytesSent)
print("image \(index + 1): \(progress)")
}
}
* 전체 코드: https://github.com/JK0369/ExMultipart
* 참고
https://m.boostcourse.org/web326/lecture/59008
https://developer.apple.com/documentation/foundation/urlsessionuploadtask
https://developer.apple.com/documentation/foundation/urlsessionuploadtask
'iOS 응용 (swift)' 카테고리의 다른 글
Comments