@ -29,196 +29,65 @@ class CloudConvert {
static let manager = CloudConvert ( )
func uploadFile ( _ url : URL ) async throws -> String {
let taskResponse = try await createJob ( url )
let uploadResponse = try await uploadFile ( taskResponse , url : url )
var fileReady = false
while fileReady = = false {
try await Task . sleep ( nanoseconds : 3_000_000_000 )
let progressResponse = try await checkFile ( id : uploadResponse . data . id )
if progressResponse . data . step = = " finish " && progressResponse . data . stepPercent = = 100 {
fileReady = true
print ( " progressResponse.data.minutes " , progressResponse . data . minutes )
}
}
let convertedFile = try await downloadConvertedFile ( id : uploadResponse . data . id )
return convertedFile
return try await createJob ( url )
}
func createJob ( _ url : URL ) async throws -> TaskResponse {
guard let taskURL = URL ( string : " https://api.convertio.co/convert " ) else {
throw CloudConvertionError . urlNotFound ( " https://api.convertio.co/convert " )
func createJob ( _ url : URL ) async throws -> String {
let apiPath = " https:// \( URLs . activationHost . rawValue ) /utils/xls-to-csv/ "
guard let taskURL = URL ( string : apiPath ) else {
throw CloudConvertionError . urlNotFound ( apiPath )
}
var request : URLRequest = URLRequest ( url : taskURL )
let parameters = " " "
{ " apikey " : " d97cf13ef6d163e5e386c381fc8d256f " , " input " : " upload " , " file " : " " , " filename " : " " , " outputformat " : " csv " , " options " : " " }
" " "
request . httpMethod = " POST "
// C r e a t e t h e b o u n d a r y s t r i n g f o r m u l t i p a r t / f o r m - d a t a
let boundary = UUID ( ) . uuidString
let postData = parameters . data ( using : . utf8 )
request . httpMethod = " POST "
request . httpBody = postData
// S e t t h e c o n t e n t t y p e t o m u l t i p a r t / f o r m - d a t a w i t h t h e b o u n d a r y
request . setValue ( " multipart/form-data; boundary= \( boundary ) " , forHTTPHeaderField : " Content-Type " )
let task = try await URLSession . shared . data ( for : request )
// p r i n t ( " t r i e d : \ ( r e q u e s t . u r l ) " )
if let errorResponse = try ? JSONDecoder ( ) . decode ( ErrorResponse . self , from : task . 0 ) {
print ( " errorResponse.error " , errorResponse . error )
throw CloudConvertionError . serviceError ( errorResponse )
}
return try JSONDecoder ( ) . decode ( TaskResponse . self , from : task . 0 )
}
// T h e f i l e t o u p l o a d
let fileName = url . lastPathComponent
let fileURL = url
func uploadFile ( _ response : TaskResponse , url : URL ) async throws -> UploadResponse {
guard let uploadTaskURL = URL ( string : " https://api.convertio.co/convert/ \( response . data . id ) / \( url . encodedLastPathComponent ) " ) else {
throw CloudConvertionError . urlNotFound ( " https://api.convertio.co/convert/ \( response . data . id ) / \( url . encodedLastPathComponent ) " )
}
var uploadRequest : URLRequest = URLRequest ( url : uploadTaskURL )
uploadRequest . httpMethod = " PUT "
let uploadTask = try await URLSession . shared . upload ( for : uploadRequest , fromFile : url )
// C o n s t r u c t t h e b o d y o f t h e r e q u e s t
var body = Data ( )
// p r i n t ( " t r i e d : \ ( u p l o a d R e q u e s t . u r l ) " )
if let errorResponse = try ? JSONDecoder ( ) . decode ( ErrorResponse . self , from : uploadTask . 0 ) {
print ( " errorResponse.error " , errorResponse . error )
throw CloudConvertionError . serviceError ( errorResponse )
}
return try JSONDecoder ( ) . decode ( UploadResponse . self , from : uploadTask . 0 )
}
func checkFile ( id : String ) async throws -> ProgressResponse {
guard let taskURL = URL ( string : " https://api.convertio.co/convert/ \( id ) /status " ) else {
throw CloudConvertionError . urlNotFound ( " https://api.convertio.co/convert/ \( id ) /status " )
}
var request : URLRequest = URLRequest ( url : taskURL )
request . httpMethod = " GET "
let task = try await URLSession . shared . data ( for : request )
// S t a r t t h e b o d y w i t h t h e b o u n d a r y a n d c o n t e n t - d i s p o s i t i o n f o r t h e f i l e
body . append ( " -- \( boundary ) \r \n " . data ( using : . utf8 ) ! )
body . append ( " Content-Disposition: form-data; name= \" file \" ; filename= \" \( fileName ) \" \r \n " . data ( using : . utf8 ) ! )
body . append ( " Content-Type: application/vnd.ms-excel \r \n \r \n " . data ( using : . utf8 ) ! )
// p r i n t ( " t r i e d : \ ( r e q u e s t . u r l ) " )
if let errorResponse = try ? JSONDecoder ( ) . decode ( ErrorResponse . self , from : task . 0 ) {
print ( " errorResponse.error " , errorResponse . error )
throw CloudConvertionError . serviceError ( errorResponse )
// A p p e n d t h e f i l e d a t a
if let fileData = try ? Data ( contentsOf : fileURL ) {
body . append ( fileData )
}
return try JSONDecoder ( ) . decode ( ProgressResponse . self , from : task . 0 )
}
func downloadConvertedFile ( id : String ) async throws -> String {
// t r y a w a i t T a s k . s l e e p ( n a n o s e c o n d s : 3 _ 0 0 0 _ 0 0 0 _ 0 0 0 )
guard let downloadTaskURL = URL ( string : " https://api.convertio.co/convert/ \( id ) /dl/base64 " ) else {
throw CloudConvertionError . urlNotFound ( " https://api.convertio.co/convert/ \( id ) /dl/base64 " )
}
var downloadRequest : URLRequest = URLRequest ( url : downloadTaskURL )
downloadRequest . httpMethod = " GET "
let downloadTask = try await URLSession . shared . data ( for : downloadRequest )
if let errorResponse = try ? JSONDecoder ( ) . decode ( ErrorResponse . self , from : downloadTask . 0 ) {
print ( " errorResponse.error " , errorResponse . error )
throw CloudConvertionError . serviceError ( errorResponse )
}
// p r i n t ( " t r i e d : \ ( d o w n l o a d R e q u e s t . u r l ) " )
let dataResponse = try JSONDecoder ( ) . decode ( DataResponse . self , from : downloadTask . 0 )
if let decodedData = Data ( base64Encoded : dataResponse . data . content ) , let string = String ( data : decodedData , encoding : . utf8 ) {
return string
// E n d t h e b o d y w i t h t h e b o u n d a r y
body . append ( " \r \n -- \( boundary ) -- \r \n " . data ( using : . utf8 ) ! )
// S e t t h e b o d y o f t h e r e q u e s t
request . httpBody = body
let ( data , response ) = try await URLSession . shared . data ( for : request )
// C h e c k t h e r e s p o n s e s t a t u s c o d e
if let httpResponse = response as ? HTTPURLResponse {
print ( " Status code: \( httpResponse . statusCode ) " )
}
throw CloudConvertionError . unknownError
// C o n v e r t t h e r e s p o n s e d a t a t o a S t r i n g
if let responseString = String ( data : data , encoding : . utf8 ) {
return responseString
} else {
let error = ErrorResponse ( code : 1 , status : " Encodage " , error : " Encodage des données de classement invalide " )
throw CloudConvertionError . serviceError ( error )
}
}
}
// MARK: - D a t a R e s p o n s e
struct DataResponse : Decodable {
let code : Int
let status : String
let data : DataDownloadClass
}
// MARK: - D a t a C l a s s
struct DataDownloadClass : Decodable {
let id , encode , content : String
}
// MARK: - E r r o r R e s p o n s e
struct ErrorResponse : Decodable {
let code : Int
let status , error : String
}
// MARK: - T a s k R e s p o n s e
struct TaskResponse : Decodable {
let code : Int
let status : String
let data : DataClass
}
// MARK: - D a t a C l a s s
struct DataClass : Decodable {
let id : String
}
// MARK: - P r o g r e s s R e s p o n s e
struct ProgressResponse : Decodable {
let code : Int
let status : String
let data : ProgressDataClass
}
// MARK: - D a t a C l a s s
struct ProgressDataClass : Decodable {
let id , step : String
let stepPercent : Int
let minutes : String
enum CodingKeys : String , CodingKey {
case id , step
case stepPercent = " step_percent "
case minutes
}
init ( from decoder : Decoder ) throws {
let container = try decoder . container ( keyedBy : CodingKeys . self )
id = try container . decode ( String . self , forKey : . id )
step = try container . decode ( String . self , forKey : . step )
minutes = try container . decode ( String . self , forKey : . minutes )
if let value = try ? container . decode ( String . self , forKey : . stepPercent ) {
print ( value )
stepPercent = Int ( value ) ? ? 0
} else {
stepPercent = try container . decode ( Int . self , forKey : . stepPercent )
}
}
}
// MARK: - O u t p u t
struct Output : Decodable {
let url : String
let size : String
}
// MARK: - U p l o a d R e s p o n s e
struct UploadResponse : Decodable {
let code : Int
let status : String
let data : UploadDataClass
}
// MARK: - D a t a C l a s s
struct UploadDataClass : Decodable {
let id , file : String
let size : Int
}
extension URL {
var encodedLastPathComponent : String {
if #available ( iOS 17.0 , * ) {
lastPathComponent
} else {
lastPathComponent . addingPercentEncoding ( withAllowedCharacters : . urlQueryAllowed ) ? ? lastPathComponent
}
}
}