You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中给FileWrapper添加Images文件夹且保存不丢失内容的实现问题

问题根因
  • 你当前fileWrapper方法每次保存都会生成全新的空目录作为MYFImages文件夹,系统收到返回的FileWrapper后会直接用这个新目录覆盖原有的整个文档包,之前存储的所有图片、子文件夹自然会被清空。
  • 你直接绕过FileDocument的管理用FileManager写入文档包路径的逻辑有问题:FileWrapper的快照机制不会感知到你手动写入的文件,保存时不会主动保留这部分内容。
  • 原有循环逻辑错误:每次循环复用同一个imageSubFolder实例,最终只会保留最后一个子文件夹。
修复方案

1. 调整MyProject结构,保留已有图片目录快照

新增属性存储读取到的图片目录FileWrapper,避免每次保存都新建空目录。

2. 修改读写逻辑,复用已有目录结构

读取文档时同步保存图片目录结构,写入时只新增缺失的子文件夹,不修改已有内容。

3. 修正图片写入逻辑

所有文档包内的修改都要同步到持有的FileWrapper实例上,保证保存时全量包含所有文件。

完整修改后代码
import UniformTypeIdentifiers

struct MyProject: FileDocument {
    var myFile: MyFile
    // 新增:存储已有图片目录的快照
    var imageDirectoryWrapper: FileWrapper?
    
    static var readableContentTypes: [UTType] { [.myf] }

    init(myFile: MyFile = MyFile()) {
        self.myFile = myFile
    }

    init(configuration: ReadConfiguration) throws {
        let decoder = JSONDecoder()
        guard let rootWrappers = configuration.file.fileWrappers,
              let data = rootWrappers["MYFProject"]?.regularFileContents else {
            throw CocoaError(.fileReadCorruptFile)
        }
        do {
            self.myFile = try decoder.decode(MyFile.self, from: data)
            // 读取已有的图片目录保存
            self.imageDirectoryWrapper = rootWrappers["MYFImages"]
        } catch {
            throw error
        }
    }
    
    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let encoder = JSONEncoder()
        do {
            let data = try encoder.encode(myFile)
            
            let mainDirectory = FileWrapper(directoryWithFileWrappers: [:])
            // 配置自定义数据文件
            let myfWrapper = FileWrapper(regularFileWithContents: data)
            myfWrapper.preferredFilename = "MYFProject"
            mainDirectory.addFileWrapper(myfWrapper)
            
            // 复用已有图片目录,不存在才新建
            let imagesWrapper = imageDirectoryWrapper ?? FileWrapper(directoryWithFileWrappers: [:])
            imagesWrapper.preferredFilename = "MYFImages"
            
            // 仅新增不存在的子文件夹,不修改已有内容
            let existingSubFolders = Set(imagesWrapper.fileWrappers?.keys ?? [])
            for numberString in myFile.numbers where !existingSubFolders.contains(numberString) {
                // 每次新建子文件夹实例,不要复用
                let newSubFolder = FileWrapper(directoryWithFileWrappers: [:])
                newSubFolder.preferredFilename = numberString
                imagesWrapper.addFileWrapper(newSubFolder)
            }
            
            mainDirectory.addFileWrapper(imagesWrapper)
            // 更新存储的快照,供下次保存复用
            self.imageDirectoryWrapper = imagesWrapper
            
            return mainDirectory
        } catch {
            throw error
        }
    }
    
    // 新增:官方推荐的图片写入方法,同步更新FileWrapper快照
    func addImage(_ imageData: Data, subFolderName: String, imageFileName: String) throws {
        guard let imagesWrapper = imageDirectoryWrapper,
              let subFolder = imagesWrapper.fileWrappers?[subFolderName] else {
            throw NSError(domain: "MyProject", code: -1, userInfo: [NSLocalizedDescriptionKey: "目标子文件夹不存在"])
        }
        let imageWrapper = FileWrapper(regularFileWithContents: imageData)
        imageWrapper.preferredFilename = imageFileName
        subFolder.addFileWrapper(imageWrapper)
    }
}
注意事项
  • 如果要保留原有直接用FileManager写入的逻辑,每次写入完成后需要重新读取整个MYFImages目录更新imageDirectoryWrapper属性,否则保存时还是会丢失手动写入的文件。
  • FileDocument的保存机制是用返回的FileWrapper全量替换整个文档包,所有需要保留的文件必须都包含在返回的目录结构中。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 02:24:01