167 lines
4.6 KiB
Swift
167 lines
4.6 KiB
Swift
import Foundation
|
|
import ExpoModulesCore
|
|
import CryptoKit
|
|
import UniformTypeIdentifiers
|
|
|
|
internal final class FileSystemFile: FileSystemPath {
|
|
init(url: URL) {
|
|
super.init(url: url, isDirectory: false)
|
|
}
|
|
|
|
override func validateType() throws {
|
|
var isDirectory: ObjCBool = false
|
|
if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) {
|
|
if isDirectory.boolValue {
|
|
throw InvalidTypeFileException()
|
|
}
|
|
}
|
|
}
|
|
|
|
func create(_ options: CreateOptions) throws {
|
|
try withCorrectTypeAndScopedAccess(permission: .write) {
|
|
try validateCanCreate(options)
|
|
do {
|
|
if options.intermediates {
|
|
try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true)
|
|
}
|
|
try? FileManager.default.removeItem(atPath: url.path)
|
|
FileManager.default.createFile(atPath: url.path, contents: nil)
|
|
} catch {
|
|
throw UnableToCreateException(error.localizedDescription)
|
|
}
|
|
}
|
|
}
|
|
|
|
override var exists: Bool {
|
|
guard checkPermission(.read) else {
|
|
return false
|
|
}
|
|
var isDirectory: ObjCBool = false
|
|
if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) {
|
|
return !isDirectory.boolValue
|
|
}
|
|
return false
|
|
}
|
|
|
|
// TODO: Move to the constructor once error is rethrowed
|
|
func validatePath() throws {
|
|
guard url.isFileURL && !url.hasDirectoryPath else {
|
|
throw Exception(name: "wrong type", description: "tried to create a file with a directory path")
|
|
}
|
|
}
|
|
|
|
var md5: String {
|
|
get throws {
|
|
return try withCorrectTypeAndScopedAccess(permission: .read) {
|
|
let fileData = try Data(contentsOf: url)
|
|
let hash = Insecure.MD5.hash(data: fileData)
|
|
return hash.map { String(format: "%02hhx", $0) }.joined()
|
|
}
|
|
}
|
|
}
|
|
|
|
var size: Int64 {
|
|
get throws {
|
|
return try getAttribute(.size, atPath: url.path)
|
|
}
|
|
}
|
|
|
|
var type: String? {
|
|
let pathExtension = url.pathExtension
|
|
if let utType = UTType(filenameExtension: pathExtension),
|
|
let mimeType = utType.preferredMIMEType {
|
|
return mimeType
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func write(_ content: String, append: Bool = false) throws {
|
|
try withCorrectTypeAndScopedAccess(permission: .write) {
|
|
if append, let data = content.data(using: .utf8) {
|
|
try writeAppending(data)
|
|
} else {
|
|
try content.write(to: url, atomically: false, encoding: .utf8) // TODO: better error handling
|
|
}
|
|
}
|
|
}
|
|
|
|
func write(_ data: Data, append: Bool = false) throws {
|
|
try withCorrectTypeAndScopedAccess(permission: .write) {
|
|
if append {
|
|
try writeAppending(data)
|
|
} else {
|
|
try data.write(to: url)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: blob support
|
|
func write(_ content: TypedArray, append: Bool = false) throws {
|
|
try withCorrectTypeAndScopedAccess(permission: .write) {
|
|
let data = Data(bytes: content.rawPointer, count: content.byteLength)
|
|
if append {
|
|
try writeAppending(data)
|
|
} else {
|
|
try data.write(to: url)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func writeAppending(_ data: Data) throws {
|
|
if !FileManager.default.fileExists(atPath: url.path) {
|
|
try data.write(to: url)
|
|
return
|
|
}
|
|
let fileHandle = try FileHandle(forWritingTo: url)
|
|
defer {
|
|
fileHandle.closeFile()
|
|
}
|
|
fileHandle.seekToEndOfFile()
|
|
fileHandle.write(data)
|
|
}
|
|
|
|
func text() throws -> String {
|
|
return try withCorrectTypeAndScopedAccess(permission: .write) {
|
|
return try String(contentsOf: url)
|
|
}
|
|
}
|
|
|
|
func bytes() throws -> Data {
|
|
return try withCorrectTypeAndScopedAccess(permission: .write) {
|
|
return try Data(contentsOf: url)
|
|
}
|
|
}
|
|
|
|
func base64() throws -> String {
|
|
return try withCorrectTypeAndScopedAccess(permission: .read) {
|
|
return try Data(contentsOf: url).base64EncodedString()
|
|
}
|
|
}
|
|
|
|
func info(options: InfoOptions) throws -> FileInfo {
|
|
return try withCorrectTypeAndScopedAccess(permission: .read) {
|
|
if !exists {
|
|
let result = FileInfo()
|
|
result.exists = false
|
|
result.uri = url.absoluteString
|
|
return result
|
|
}
|
|
switch url.scheme {
|
|
case "file":
|
|
let result = FileInfo()
|
|
result.exists = true
|
|
result.uri = url.absoluteString
|
|
result.size = try size
|
|
result.modificationTime = try modificationTime
|
|
result.creationTime = try creationTime
|
|
if options.md5 {
|
|
result.md5 = try md5
|
|
}
|
|
return result
|
|
default:
|
|
throw UnableToGetInfoException("url scheme \(String(describing: url.scheme)) is not supported")
|
|
}
|
|
}
|
|
}
|
|
}
|