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

如何将SwiftUI生成的PDF上传至Firebase Storage并获取下载链接

如何将生成的PDF上传至Firebase Storage并获取下载链接

我已经实现了SwiftUI视图转PDF的功能,现在需要把生成的PDF上传到Firebase Storage,并且获取它的下载链接。

已实现的PDF生成代码

func exportToPDF() {
    let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let outputFileURL = documentDirectory.appendingPathComponent("SwiftUI.pdf")

    //Normal width
    let width: CGFloat = 8.5 * 72.0
    //Estimate the height of your view
    let height: CGFloat = 1000
    let charts = SmartSheet()

    let pdfVC = UIHostingController(rootView: charts)
    pdfVC.view.frame = CGRect(x: 0, y: 0, width: width, height: height)

    //Render the view behind all other views
    let rootVC = UIApplication.shared.windows.first?.rootViewController
    rootVC?.addChild(pdfVC)
    rootVC?.view.insertSubview(pdfVC.view, at: 0)

    //Render the PDF
    let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: 8.5 * 72.0, height: height))
    DispatchQueue.main.async {
        do {
            try pdfRenderer.writePDF(to: outputFileURL, withActions: { (context) in
                context.beginPage()
                pdfVC.view.layer.render(in: context.cgContext)
            })
            print("wrote file to: \(outputFileURL.path)")
        } catch {
            print("Could not create PDF file: \(error.localizedDescription)")
        }
    }

    pdfVC.removeFromParent()
    pdfVC.view.removeFromSuperview()
}

已实现的UIImage上传及获取链接代码

func upload(image: UIImage) {
    // Create a storage reference
    let storageRef = Storage.storage().reference().child("squawks/\(v.FirstName)-sqauwk.jpg")

    // Convert the image into JPEG and compress the quality to reduce its size
    let data = image.jpegData(compressionQuality: 0.2)

    // Change the content type to jpg. If you don't, it'll be saved as application/octet-stream type
    let metadata = StorageMetadata()
    metadata.contentType = "image/jpeg"

    // Upload the image
    if let data = data {
        storageRef.putData(data, metadata: metadata) { (metadata, error) in
            if let error = error {
                print("Error while uploading file: ", error)
            }

            if let metadata = metadata {
                print("Metadata: ", metadata)
            }
        }
    }
}


func getPhotoURL(completion: @escaping (String) -> Void) {
    // Create a reference to the file you want to download
    let starsRef = Storage.storage().reference().child("squawks/\(v.FirstName)-sqauwk.jpg")

    // Fetch the download URL
    starsRef.downloadURL { url, error in
        if let error = error {
            // Handle any errors
            print("error getting link: \(error)")
        } else {
            // Get the download URL for 'images/stars.jpg'
            completion(url!.absoluteString)
        }
    }
}

//On click of button:
var strURL = ""
upload(image: inputImage!) //inputImage is a UIImage
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    getPhotoURL() { url in
        strURL = url
        print("STORAGE URL: \(strURL)")
    }
}

解决方案

参考图片上传逻辑,对PDF上传做适配,步骤如下:

1. 修改PDF生成函数,添加回调返回文件URL

让exportToPDF支持回调,生成PDF后直接触发后续上传流程:

func exportToPDF(completion: @escaping (URL?, Error?) -> Void) {
    let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let outputFileURL = documentDirectory.appendingPathComponent("SwiftUI.pdf")

    let width: CGFloat = 8.5 * 72.0
    let height: CGFloat = 1000
    let charts = SmartSheet()

    let pdfVC = UIHostingController(rootView: charts)
    pdfVC.view.frame = CGRect(x: 0, y: 0, width: width, height: height)

    let rootVC = UIApplication.shared.windows.first?.rootViewController
    rootVC?.addChild(pdfVC)
    rootVC?.view.insertSubview(pdfVC.view, at: 0)

    let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: width, height: height))
    DispatchQueue.main.async {
        do {
            try pdfRenderer.writePDF(to: outputFileURL, withActions: { (context) in
                context.beginPage()
                pdfVC.view.layer.render(in: context.cgContext)
            })
            print("wrote file to: \(outputFileURL.path)")
            completion(outputFileURL, nil)
        } catch {
            print("Could not create PDF file: \(error.localizedDescription)")
            completion(nil, error)
        }
        
        pdfVC.removeFromParent()
        pdfVC.view.removeFromSuperview()
    }
}

2. 实现PDF上传函数

读取PDF文件数据,设置正确的MIME类型(application/pdf):

func uploadPDF(at url: URL, completion: @escaping (Error?) -> Void) {
    // 创建Storage引用,命名可按需调整
    let pdfName = "\(v.FirstName)-sheet.pdf"
    let storageRef = Storage.storage().reference().child("pdfs/\(pdfName)")
    
    // 读取PDF文件数据
    do {
        let pdfData = try Data(contentsOf: url)
        
        // 设置Metadata,指定内容类型为PDF
        let metadata = StorageMetadata()
        metadata.contentType = "application/pdf"
        
        // 上传数据
        storageRef.putData(pdfData, metadata: metadata) { (_, error) in
            if let error = error {
                print("PDF上传失败: \(error.localizedDescription)")
                completion(error)
            } else {
                print("PDF上传成功")
                completion(nil)
            }
        }
    } catch {
        print("读取PDF文件失败: \(error.localizedDescription)")
        completion(error)
    }
}

3. 实现获取PDF下载链接的函数

修改Storage路径即可复用类似逻辑:

func getPDFDownloadURL(completion: @escaping (String?, Error?) -> Void) {
    let pdfName = "\(v.FirstName)-sheet.pdf"
    let pdfRef = Storage.storage().reference().child("pdfs/\(pdfName)")
    
    pdfRef.downloadURL { url, error in
        if let error = error {
            print("获取PDF链接失败: \(error.localizedDescription)")
            completion(nil, error)
        } else {
            completion(url?.absoluteString, nil)
        }
    }
}

4. 完整调用流程(按钮点击事件)

用回调替代asyncAfter等待,逻辑更可靠:

// 按钮点击时触发
exportToPDF { pdfURL, error in
    guard let pdfURL = pdfURL, error == nil else { return }
    
    uploadPDF(at: pdfURL) { uploadError in
        guard uploadError == nil else { return }
        
        // 上传成功后获取下载链接
        getPDFDownloadURL { url, urlError in
            if let downloadURL = url {
                print("PDF下载链接: \(downloadURL)")
                // 可在此将链接保存到数据库或直接使用
            }
        }
    }
}

注意事项

  • 不要依赖asyncAfter等待上传完成,回调方式更适配网络波动场景。
  • 确保Firebase Storage规则允许PDF的上传与读取,测试阶段可使用宽松规则(上线后需调整为安全规则):
    rules_version = '2';
    service firebase.storage {
      match /b/{bucket}/o {
        match /{allPaths=**} {
          allow read, write: if request.auth != null;
        }
      }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:35:23