Home telegram - tempBox
Post
Cancel

telegram - tempBox

TempBoxKey

struct TempBoxKey

1
2
3
let path: String?
let fileName: String
let uniqueId: Int?

TempBoxFile

class *TempBoxFile**

1
2
3
fileprivate let key: TempBoxKey
fileprivate let id: Int
public let path: String

TempBoxFileContext

class TempBoxFileContext

1
2
3
4
5
6
7
let directory: String
let fileName: String
var subscribers = Set<Int>()

var path: String {
    return self.directory + "/" + self.fileName
}

TempBoxContexts

class TempBoxContexts

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
private var nextId: Int = 0
private var contexts: [TempBoxKey: TempBoxFileContext] = [:]

// generate a tempboxfile instance
func file(basePath: String, path: String, fileName: String) -> TempBoxFile {
    let key = TempBoxKey(path: path, fileName: fileName, uniqueId: nil)
    let context: TempBoxFileContext
    if let current = self.contexts[key] {
        context = current
    } else {
        let id = self.nextId
        self.nextId += 1
        let dirName = "\(id)"
        let dirPath = basePath + "/" + dirName
        var cleanName = fileName
        if cleanName.hasPrefix("..") {
            cleanName = "__" + String(cleanName[cleanName.index(cleanName.startIndex, offsetBy: 2)])
        }
        cleanName = cleanName.replacingOccurrences(of: "/", with: "_")
        context = TempBoxFileContext(directory: dirPath, fileName: cleanName)
        self.contexts[key] = context
        let _ = try? FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil)
        let _ = try? FileManager.default.linkItem(atPath: path, toPath: context.path)
    }
    let id = self.nextId
    self.nextId += 1
    context.subscribers.insert(id)
    return TempBoxFile(key: key, id: id, path: context.path)
}

// generate a tempboxfile instance
func tempFile(basePath: String, fileName: String) -> TempBoxFile {
    let id = self.nextId
    self.nextId += 1
    
  // key = f(path, filename, uniqueId)
    let key = TempBoxKey(path: nil, fileName: fileName, uniqueId: id)
    let context: TempBoxFileContext
    
    let dirName = "\(id)"
  // dirPath = ${tempbox.basepath}/temp/${processType}/temp-${String(UInt64(bitPattern: launchSpecificId), radix: 16)}/${id}
    let dirPath = basePath + "/" + dirName
  
  // cleanName = f(filename)
    var cleanName = fileName
    if cleanName.hasPrefix("..") {
        cleanName = "__" + String(cleanName[cleanName.index(cleanName.startIndex, offsetBy: 2)])
    }
    cleanName = cleanName.replacingOccurrences(of: "/", with: "_")
    context = TempBoxFileContext(directory: dirPath, fileName: cleanName)
    self.contexts[key] = context
    let _ = try? FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil)
    context.subscribers.insert(id)
  // context.path = ${tempbox.basepath}/temp/${processType}/temp-${String(UInt64(bitPattern: launchSpecificId), radix: 16)}/${id}/${cleanName}
    return TempBoxFile(key: key, id: id, path: context.path)
}

// dispose
func dispose(_ file: TempBoxFile) -> [String] {
    if let context = self.contexts[file.key] {
        context.subscribers.remove(file.id)
        if context.subscribers.isEmpty {
            self.contexts.removeValue(forKey: file.key)
            return [context.directory]
        }
    }
    return []
}

TempBox

class TempBox

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
private let basePath: String
private let processType: String
private let launchSpecificId: Int64
private let currentBasePath: String

private let contexts = Atomic<TempBoxContexts>(value: TempBoxContexts())

public static func initializeShared(basePath: String, processType: String, launchSpecificId: Int64) {
    sharedValue = TempBox(basePath: basePath, processType: processType, launchSpecificId: launchSpecificId)
}

public static var shared: TempBox {
    return sharedValue!
}

private init(basePath: String, processType: String, launchSpecificId: Int64) {
    self.basePath = basePath
    self.processType = processType
    self.launchSpecificId = launchSpecificId
    
  // basePath/temp/${processType}/temp-${String(UInt64(bitPattern: launchSpecificId), radix: 16)}
    self.currentBasePath = basePath + "/temp/" + processType + "/temp-" + String(UInt64(bitPattern: launchSpecificId), radix: 16)
    self.cleanupPreviousLaunches(path: basePath + "/temp/" + processType, currentLaunchSpecificId: launchSpecificId)
}

// clearnup previous launches
private func cleanupPreviousLaunches(path: String, currentLaunchSpecificId: Int64) {
    DispatchQueue.global(qos: .background).async {
        let currentName = "temp-" + String(UInt64(bitPattern: currentLaunchSpecificId), radix: 16)
        if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: path), includingPropertiesForKeys: [], options: []) {
            for url in files {
              // basePath/temp/${processType}/temp-
                if url.lastPathComponent.hasPrefix("temp-") && url.lastPathComponent != currentName {
                    let _ = try? FileManager.default.removeItem(atPath: url.path)
                }
            }
        }
    }
}

// generate a temp box file instance
public func file(path: String, fileName: String) -> TempBoxFile {
    return self.contexts.with { contexts in
        return contexts.file(basePath: self.currentBasePath, path: path, fileName: fileName)
    }
}

// generate a temp box file instance
public func tempFile(fileName: String) -> TempBoxFile {
    return self.contexts.with { contexts in
        return contexts.tempFile(basePath: self.currentBasePath, fileName: fileName)
    }
}

// dispose a temp box file 
public func dispose(_ file: TempBoxFile) {
    let removePaths = self.contexts.with { contexts in
        return contexts.dispose(file)
    }
    if !removePaths.isEmpty {
        DispatchQueue.global(qos: .background).async {
            for path in removePaths {
                let _ = try? FileManager.default.removeItem(atPath: path)
            }
        }
    }
}
This post is licensed under CC BY 4.0 by the author.