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

SwiftUI如何将相机拍摄的照片直接上传至服务器(非相册选取)

实现方案

你现有代码里已经在拍摄完成时拿到了图片原始二进制数据,完全不需要走相册选取的流程,只要打通相机模块和上传模块的数据流,加个直接上传的入口即可,改造成本很低。

核心改造点

  • 你的CameraModel类在拍摄回调里已经把图片数据存在了@Published var picData = Data(count:0)属性中,这部分数据可以直接用来上传,不需要先写入系统相册再重新读取。
  • 把上传逻辑抽成公共可复用方法,相册选图、相机拍图两种场景都能调用,避免写重复代码。
  • 在相机拍摄完成后的操作栏加「上传」按钮,不需要强制用户先保存到相册再跳转选图。

具体代码修改

第一步:抽离公共上传方法

把原来写在PictureView里的上传逻辑抽成独立方法,减少冗余转码步骤:

import Foundation
import UIKit

// 可放到公共工具类中,全局可调用
func uploadImage(imageData: Data, completion: @escaping (Bool, String?) -> Void) {
    // 直接用传入的原始图片数据转base64,避免反复转Image/UIImage造成画质损失
    let imageStr = imageData.base64EncodedString()
    
    guard let url = URL(string: "gs://lalala-af09c.appspot.com/swiftui-save-image.php") else {
        completion(false, "上传地址无效")
        return
    }
    
    let paramStr = "image=\(imageStr)"
    guard let paramData = paramStr.data(using: .utf8) else {
        completion(false, "参数编码失败")
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = paramData
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            completion(false, error.localizedDescription)
            return
        }
        guard let data = data else {
            completion(false, "服务端返回数据为空")
            return
        }
        let responseText = String(data: data, encoding: .utf8) ?? ""
        completion(true, responseText)
    }.resume()
}

第二步:给相机页面添加上传入口

在拍摄完成后的操作栏加直接上传按钮,复用已经拿到的picData,不需要跳转相册页面:

// 只贴CameraView中需要修改的底部按钮栏代码,其余原有拍摄、重拍、保存相册逻辑保持不变
HStack{
    if camera.isTaken{
        Button(action: {
            if !camera.isSaved{
                camera.savePic()
            }
        }, label: {
            Text(camera.isSaved ? "Saved" : "Save")
                .foregroundColor(.black)
                .fontWeight(.semibold)
                .padding(.vertical,10)
                .padding(.horizontal,20)
                .background(Color.white)
                .clipShape(Capsule())
        }).padding(.leading)
        
        // 新增直接上传按钮
        Button(action: {
            guard !camera.picData.isEmpty else { return }
            uploadImage(imageData: camera.picData) { success, msg in
                DispatchQueue.main.async {
                    // 主线程更新UI,可加成功/失败提示
                    print(success ? "上传成功:\(msg ?? "")" : "上传失败:\(msg ?? "")")
                }
            }
        }, label: {
            Text("Upload")
                .foregroundColor(.white)
                .fontWeight(.semibold)
                .padding(.vertical,10)
                .padding(.horizontal,20)
                .background(Color.blue)
                .clipShape(Capsule())
        })
        
        Spacer()
        
    }else{
        Button(action: camera.takePic, label: {
            ZStack{
                Circle()
                    .fill(Color.white)
                    .frame(width: 65, height: 65, alignment: .center)
                Circle()
                    .stroke(Color.white,lineWidth: 2)
                    .frame(width: 75, height: 75, alignment: .center)
            }
        })
    }
}.frame(height: 75)

第三步:可选优化

  • 如果需要拍完自动上传,不需要用户点按钮,直接把上传逻辑加到CameraModel的photoOutput(_:didFinishProcessingPhoto:error:)代理方法里,拿到picData后直接调用上传方法即可。
  • 原有相册选图上传的逻辑也可以直接复用uploadImage方法,删掉原来冗余的Image转UIImage的代码,在ImagePicker选图时直接存原始UIImage的JPEG数据即可,减少性能损耗。
  • 保存到相册的逻辑可以保留,给用户选择权,不需要强制写入相册才能上传。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 08:12:47