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

Swift中含"/"字符的自定义文件导出目录问题解决

解决含"/"字符的自定义文件导出时创建多级目录的问题

问题描述

应用自定义文件类型,导出名称包含"/"字符的文件时会失败,且自动生成多级目录结构。例如文件名1/2/3.stitch会被解析为file:///Users/nicholasarner/Library/Containers/app.stitchdesign.stitch/Data/tmp/1/2/3.stitch,导致创建不必要的子目录,最终导出出错。

问题原因

URL.appendingPathComponent(_:)方法会将文件名中的"/"识别为路径分隔符,自动拆分生成多级目录,而非将其作为文件名的一部分处理。

解决方案

提供两种可行方案,可根据需求选择:

方案1:替换为全角斜杠(视觉近似,实现简单)

将文件名中的半角"/"替换为全角"/",既保留视觉上的一致感,又不会被系统识别为路径分隔符,无需额外编码解码操作。

修改后的代码:

static func exportDocument(_ document: StitchDocument) async -> SentTransferredFile {
    log("StitchDocumentWrapper: transferRepresentation: exporting: called")

    let projectURL = document.getUrl()
    
    // 替换半角"/"为全角"/",避免被解析为路径分隔符
    let exportedFileName = (document.name + ".stitch").replacingOccurrences(of: "/", with: "/")
    
    let tempExportURL = StitchFileManager.tempDir.appendingPathComponent(exportedFileName)

    log("StitchDocumentWrapper: transferRepresentation: projectURL: \(projectURL)")
    log("StitchDocumentWrapper: transferRepresentation: tempExportURL: \(tempExportURL)")

    do {
        try? FileManager.default.removeItem(at: tempExportURL)
        try FileManager.default.zipItem(at: projectURL, to: tempExportURL)
        return SentTransferredFile(tempExportURL)
    } catch {
        log("StitchDocumentWrapper: transferRepresentation: FAILED: error: \(error)")
        return SentTransferredFile(tempExportURL)
    }
}

方案2:百分号编码(严格保留原字符语义)

对文件名进行百分号编码,将"/"转换为%2F,确保系统将其视为文件名的一部分而非路径分隔符。后续展示给用户时,再解码恢复原文件名。

修改后的代码:

static func exportDocument(_ document: StitchDocument) async -> SentTransferredFile {
    log("StitchDocumentWrapper: transferRepresentation: exporting: called")

    let projectURL = document.getUrl()
    let originalFileName = document.name + ".stitch"
    
    // 对文件名编码,排除"/"作为路径分隔符的可能
    guard let encodedFileName = originalFileName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed.subtracting(CharacterSet(charactersIn: "/"))) else {
        // 编码失败时使用 fallback 文件名
        let fallbackURL = StitchFileManager.tempDir.appendingPathComponent("exported.stitch")
        return SentTransferredFile(fallbackURL)
    }
    
    let tempExportURL = StitchFileManager.tempDir.appendingPathComponent(encodedFileName)

    log("StitchDocumentWrapper: transferRepresentation: projectURL: \(projectURL)")
    log("StitchDocumentWrapper: transferRepresentation: tempExportURL: \(tempExportURL)")

    do {
        try? FileManager.default.removeItem(at: tempExportURL)
        try FileManager.default.zipItem(at: projectURL, to: tempExportURL)
        
        // 若需展示原文件名,在此处解码:
        // let decodedFileName = encodedFileName.removingPercentEncoding ?? originalFileName
        return SentTransferredFile(tempExportURL)
    } catch {
        log("StitchDocumentWrapper: transferRepresentation: FAILED: error: \(error)")
        return SentTransferredFile(tempExportURL)
    }
}

方案对比

  • 全角替换:操作简单,用户在文件系统中看到的文件名视觉上与原名称一致;缺点是字符本身与原半角"/"不同,部分场景下可能存在兼容性问题。
  • 百分号编码:严格保留原字符语义,解码后与原文件名完全一致;缺点是文件系统中存储的文件名是编码后的格式(如1%2F2%2F3.stitch),不够直观,需额外解码步骤展示给用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 17:55:23