GBA003/External/Harmony/Backends/Drive/Harmony-Drive/DriveService+Files.swift
2024-05-30 10:22:15 +08:00

181 lines
7.1 KiB
Swift

//
// DriveService+Files.swift
// Harmony-Drive
//
// Created by Riley Testut on 10/24/18.
// Copyright © 2018 Riley Testut. All rights reserved.
//
import Foundation
import CoreData
import Harmony
import Roxas
import GoogleAPIClientForREST
public extension DriveService
{
func upload(_ file: File, for record: AnyRecord, metadata: [HarmonyMetadataKey: Any], context: NSManagedObjectContext, completionHandler: @escaping (Result<RemoteFile, FileError>) -> Void) -> Progress
{
let progress = Progress.discreteProgress(totalUnitCount: 1)
progress.kind = .file
do
{
guard let attributes = try? FileManager.default.attributesOfItem(atPath: file.fileURL.path), let size = attributes[.size] as? Int64 else { throw FileError.doesNotExist(file.identifier) }
progress.totalUnitCount = size
let filename = String(describing: record.recordID) + "-" + file.identifier
let fetchQuery = GTLRDriveQuery_FilesList.query()
fetchQuery.q = "name = '\(filename)'"
fetchQuery.fields = "nextPageToken, files(\(fileQueryFields))"
fetchQuery.spaces = appDataFolder
let ticket = self.service.executeQuery(fetchQuery) { (ticket, object, error) in
do
{
let files = try self.process(Result((object as? GTLRDrive_FileList)?.files, error))
let driveFile = GTLRDrive_File()
driveFile.name = filename
driveFile.mimeType = "application/octet-stream"
driveFile.appProperties = GTLRDrive_File_AppProperties(json: metadata)
let uploadParameters = GTLRUploadParameters(fileURL: file.fileURL, mimeType: "application/octet-stream")
let uploadQuery: GTLRDriveQuery
if let file = files.first, let identifier = file.identifier
{
uploadQuery = GTLRDriveQuery_FilesUpdate.query(withObject: driveFile, fileId: identifier, uploadParameters: uploadParameters)
}
else
{
driveFile.parents = [appDataFolder]
uploadQuery = GTLRDriveQuery_FilesCreate.query(withObject: driveFile, uploadParameters: uploadParameters)
}
uploadQuery.fields = fileQueryFields
uploadQuery.executionParameters.uploadProgressBlock = { (ticket, uploadedBytes, totalBytes) in
progress.completedUnitCount = Int64(min(uploadedBytes, totalBytes))
}
let ticket = self.service.executeQuery(uploadQuery) { (ticket, driveFile, error) in
context.perform {
do
{
let driveFile = try self.process(Result(driveFile as? GTLRDrive_File, error))
guard let remoteFile = RemoteFile(file: driveFile, context: context) else {
throw ServiceError.invalidResponse
}
completionHandler(.success(remoteFile))
}
catch
{
completionHandler(.failure(FileError(file.identifier, error)))
}
}
}
progress.cancellationHandler = {
ticket.cancel()
completionHandler(.failure(.other(file.identifier, GeneralError.cancelled)))
}
}
catch
{
completionHandler(.failure(FileError(file.identifier, error)))
}
}
progress.cancellationHandler = {
ticket.cancel()
completionHandler(.failure(.other(file.identifier, GeneralError.cancelled)))
}
}
catch
{
completionHandler(.failure(FileError(file.identifier, error)))
}
return progress
}
func download(_ remoteFile: RemoteFile, completionHandler: @escaping (Result<File, FileError>) -> Void) -> Progress
{
let progress = Progress.discreteProgress(totalUnitCount: 1)
progress.totalUnitCount = Int64(remoteFile.size)
progress.kind = .file
let fileIdentifier = remoteFile.identifier
let query = GTLRDriveQuery_RevisionsGet.queryForMedia(withFileId: remoteFile.remoteIdentifier, revisionId: remoteFile.versionIdentifier)
let downloadRequest = self.service.request(for: query) as URLRequest
let fileURL = FileManager.default.uniqueTemporaryURL()
let fetcher = self.service.fetcherService.fetcher(with: downloadRequest)
fetcher.destinationFileURL = fileURL
fetcher.downloadProgressBlock = { (bytesWritten, totalBytesWritten, totalBytes) in
progress.completedUnitCount = totalBytesWritten
}
fetcher.beginFetch { (_, error) in
do
{
try self.process(Result(error))
guard FileManager.default.fileExists(atPath: fileURL.path) else { throw ServiceError.invalidResponse }
let file = File(identifier: fileIdentifier, fileURL: fileURL)
completionHandler(.success(file))
}
catch
{
completionHandler(.failure(FileError(fileIdentifier, error)))
}
}
progress.cancellationHandler = {
fetcher.stopFetching()
completionHandler(.failure(.other(fileIdentifier, GeneralError.cancelled)))
}
return progress
}
func delete(_ remoteFile: RemoteFile, completionHandler: @escaping (Result<Void, FileError>) -> Void) -> Progress
{
let progress = Progress.discreteProgress(totalUnitCount: 1)
let fileIdentifier = remoteFile.identifier
let query = GTLRDriveQuery_FilesDelete.query(withFileId: fileIdentifier)
let ticket = self.service.executeQuery(query) { (ticket, file, error) in
do
{
try self.process(Result(error))
completionHandler(.success)
}
catch
{
completionHandler(.failure(FileError(fileIdentifier, error)))
}
}
progress.cancellationHandler = {
ticket.cancel()
completionHandler(.failure(.other(fileIdentifier, GeneralError.cancelled)))
}
return progress
}
}