@hanwha-ss1/plugin 0.7.1 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,6 +33,71 @@ public class DownloadPlugin: CAPPlugin {
33
33
  }
34
34
  }
35
35
  }
36
+
37
+ @objc func fileDownloadChunk(_ call: CAPPluginCall) {
38
+ guard let fileName = call.getString("fileName"),
39
+ let chunkData = call.getString("chunkData") else {
40
+ call.resolve([
41
+ "result": false,
42
+ "message": "fileName 또는 chunkData 누락"
43
+ ])
44
+ return
45
+ }
46
+
47
+ let chunkIndex = call.getInt("chunkIndex") ?? 0
48
+ let totalChunks = call.getInt("totalChunks") ?? 1
49
+ let isLastChunk = call.getBool("isLastChunk") ?? false
50
+
51
+ // ✅ Data URL 처리 (base64, 이후 부분만 추출)
52
+ let pureBase64: String
53
+ if let range = chunkData.range(of: "base64,") {
54
+ pureBase64 = String(chunkData[range.upperBound...])
55
+ } else {
56
+ pureBase64 = chunkData
57
+ }
58
+
59
+ // Base64 → Data 변환
60
+ guard let data = Data(base64Encoded: pureBase64, options: .ignoreUnknownCharacters) else {
61
+ call.resolve([
62
+ "result": false,
63
+ "message": "청크 데이터(Base64) 디코딩 실패"
64
+ ])
65
+ return
66
+ }
67
+
68
+ // 다운로드 생성 (최초 호출 시만)
69
+ if chunkIndex == 0 {
70
+ _ = DownloadService.createDownload(id: fileName, totalChunks: totalChunks)
71
+ }
72
+
73
+ // 오프셋 = 청크 크기 * 인덱스
74
+ let offset = UInt64(data.count * chunkIndex)
75
+ DownloadService.appendChunk(id: fileName, data: data, offset: offset)
76
+
77
+ if isLastChunk {
78
+ if DownloadService.isComplete(id: fileName),
79
+ let finalURL = DownloadService.finishDownload(id: fileName) {
80
+ call.resolve([
81
+ "result": true,
82
+ "message": "청크 다운로드 완료",
83
+ "path": finalURL.path
84
+ ])
85
+ return
86
+ } else {
87
+ call.resolve([
88
+ "result": false,
89
+ "message": "마지막 청크 처리 중 오류 발생"
90
+ ])
91
+ return
92
+ }
93
+ }
94
+
95
+ // 마지막 청크가 아니면 그냥 성공 반환
96
+ call.resolve([
97
+ "result": true,
98
+ "message": "청크 \(chunkIndex + 1)/\(totalChunks) 저장 완료"
99
+ ])
100
+ }
36
101
 
37
102
  /// 대용량 Base64 문자열을 스트리밍 방식으로 파일로 변환
38
103
  private func decodeBase64ToFile(base64: String, fileURL: URL) -> Bool {
@@ -0,0 +1,105 @@
1
+ import Foundation
2
+ import UIKit
3
+ import Capacitor
4
+
5
+ public class DownloadService: NSObject {
6
+
7
+ private class ChunkDownloadInfo {
8
+ var tempFile: URL
9
+ var totalChunks: Int
10
+ var receivedChunks: Int
11
+ var fileHandle: FileHandle?
12
+
13
+ init(tempFile: URL, totalChunks: Int) {
14
+ self.tempFile = tempFile
15
+ self.totalChunks = totalChunks
16
+ self.receivedChunks = 0
17
+
18
+ if !FileManager.default.fileExists(atPath: tempFile.path) {
19
+ FileManager.default.createFile(atPath: tempFile.path, contents: nil, attributes: nil)
20
+ }
21
+
22
+ do {
23
+ self.fileHandle = try FileHandle(forWritingTo: tempFile)
24
+ } catch {
25
+ print("❌ 파일 핸들 생성 실패: \(error)")
26
+ }
27
+ }
28
+
29
+ deinit {
30
+ if #available(iOS 13.0, *) {
31
+ try? fileHandle?.close()
32
+ }
33
+ }
34
+ }
35
+
36
+ private static var chunkDownloads: [String: ChunkDownloadInfo] = [:]
37
+ private static let queue = DispatchQueue(label: "DownloadServiceQueue", attributes: .concurrent)
38
+
39
+ // MARK: - 다운로드 생성 (Documents 경로에 .tmp 생성)
40
+ public static func createDownload(id: String, totalChunks: Int) -> URL? {
41
+ let docsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
42
+ let tempURL = docsURL.appendingPathComponent("\(id).tmp")
43
+
44
+ let info = ChunkDownloadInfo(tempFile: tempURL, totalChunks: totalChunks)
45
+
46
+ queue.async(flags: .barrier) {
47
+ chunkDownloads[id] = info
48
+ }
49
+
50
+ return tempURL
51
+ }
52
+
53
+ // MARK: - 청크 추가
54
+ public static func appendChunk(id: String, data: Data, offset: UInt64) {
55
+ queue.async(flags: .barrier) {
56
+ guard let info = chunkDownloads[id],
57
+ let handle = info.fileHandle else { return }
58
+
59
+ do {
60
+ if #available(iOS 13.0, *) {
61
+ try handle.seek(toOffset: offset)
62
+ }
63
+ handle.write(data)
64
+ info.receivedChunks += 1
65
+ } catch {
66
+ print("❌ 청크 쓰기 실패: \(error)")
67
+ }
68
+ }
69
+ }
70
+
71
+ // MARK: - 완료 여부 확인
72
+ public static func isComplete(id: String) -> Bool {
73
+ var complete = false
74
+ queue.sync {
75
+ if let info = chunkDownloads[id] {
76
+ complete = info.receivedChunks >= info.totalChunks
77
+ }
78
+ }
79
+ return complete
80
+ }
81
+
82
+ // MARK: - 다운로드 마무리 (tmp → 최종 파일명으로 이동)
83
+ public static func finishDownload(id: String) -> URL? {
84
+ var finalURL: URL?
85
+ queue.sync(flags: .barrier) {
86
+ if let info = chunkDownloads.removeValue(forKey: id) {
87
+ let docsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
88
+ let targetURL = docsURL.appendingPathComponent(id) // 최종 파일 이름
89
+
90
+ do {
91
+ // 기존 파일 삭제
92
+ if FileManager.default.fileExists(atPath: targetURL.path) {
93
+ try FileManager.default.removeItem(at: targetURL)
94
+ }
95
+ // tmp → 최종 경로로 이동
96
+ try FileManager.default.moveItem(at: info.tempFile, to: targetURL)
97
+ finalURL = targetURL
98
+ } catch {
99
+ print("❌ 파일 이동 실패: \(error)")
100
+ }
101
+ }
102
+ }
103
+ return finalURL
104
+ }
105
+ }
@@ -12,6 +12,7 @@ CAP_PLUGIN(Plugin, "Plugin",
12
12
  CAP_PLUGIN_METHOD(open, CAPPluginReturnPromise);
13
13
  CAP_PLUGIN_METHOD(edgeSwipe, CAPPluginReturnPromise);
14
14
  CAP_PLUGIN_METHOD(fileDownload, CAPPluginReturnPromise);
15
+ CAP_PLUGIN_METHOD(fileDownloadChunk, CAPPluginReturnPromise);
15
16
  CAP_PLUGIN_METHOD(doDisabledCapture, CAPPluginReturnPromise);
16
17
  CAP_PLUGIN_METHOD(addKeyboardMenu, CAPPluginReturnPromise);
17
18
  CAP_PLUGIN_METHOD(removeKeyboardMenu, CAPPluginReturnPromise);
@@ -46,15 +46,11 @@ public class Plugin: CAPPlugin {
46
46
  안드로이드에서만 사용
47
47
  */
48
48
  @objc func executeApp(_ call: CAPPluginCall) {
49
-
50
-
51
- if let url = URL(string: call.getString("package") ?? "") {
49
+ if let url = URL(string: call.getString("package") ?? "") {
52
50
  DispatchQueue.main.async {
53
51
  UIApplication.shared.open(url, options: [:], completionHandler: nil)
54
52
  }
55
53
  }
56
-
57
-
58
54
  }
59
55
 
60
56
  @objc func auth(_ call: CAPPluginCall) {
@@ -69,9 +65,6 @@ public class Plugin: CAPPlugin {
69
65
  KeyboardMenuPlugin().remove(call)
70
66
  }
71
67
 
72
-
73
-
74
-
75
68
  @objc func addContact(_ call: CAPPluginCall) {
76
69
  ContactPlugin().addContact(call)
77
70
  }
@@ -97,6 +90,10 @@ public class Plugin: CAPPlugin {
97
90
  DownloadPlugin().doDownload(call, self.bridge!)
98
91
  }
99
92
 
93
+ @objc func fileDownloadChunk(_ call: CAPPluginCall) {
94
+ DownloadPlugin().fileDownloadChunk(call)
95
+ }
96
+
100
97
  @objc func doDisabledCapture(_ call: CAPPluginCall) {
101
98
  CapturePlugin().doDiabledCapture(call, self.bridge!)
102
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanwha-ss1/plugin",
3
- "version": "0.7.1",
3
+ "version": "0.7.4",
4
4
  "description": "Plugin",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",