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

iOS Swift4新手求助:如何携带JSON参数以ByteArray形式发送图片

嘿,我完全懂你现在的挫败感——刚入门iOS编程就撞上「用户数据+图片一起上传」的难题,搜了一周都没解决确实闹心!别慌,我给你一套Swift4下的完整解决方案,一步步来就能搞定:

第一步:先搞定相机/相册的图片选择

首先得能让用户选到图片,用UIImagePickerController就能实现,记得遵守对应的协议:

class UserCreationVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    // 绑定到"选择头像"按钮的点击事件
    @IBAction func pickAvatarTapped(_ sender: UIButton) {
        let picker = UIImagePickerController()
        picker.delegate = self
        picker.allowsEditing = true // 允许用户裁剪图片,体验更好

        // 弹出选项让用户选相机还是相册
        let actionSheet = UIAlertController(title: "选择头像来源", message: nil, preferredStyle: .actionSheet)
        
        actionSheet.addAction(UIAlertAction(title: "拍摄照片", style: .default) { _ in
            guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
                let alert = UIAlertController(title: "提示", message: "当前设备不支持相机", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "确定", style: .default))
                self.present(alert, animated: true)
                return
            }
            picker.sourceType = .camera
            self.present(picker, animated: true)
        })
        
        actionSheet.addAction(UIAlertAction(title: "从相册选择", style: .default) { _ in
            picker.sourceType = .photoLibrary
            self.present(picker, animated: true)
        })
        
        actionSheet.addAction(UIAlertAction(title: "取消", style: .cancel))
        present(actionSheet, animated: true)
    }

    // 获取选中的图片
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        picker.dismiss(animated: true)
        
        // 优先取编辑后的图片,没有的话取原图
        guard let selectedImage = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage else {
            print("未选中有效图片")
            return
        }

        // 拿到图片后,调用上传方法(这里的userData换成你实际要传的用户信息)
        let userData = [
            "username": "newUser123",
            "email": "user@example.com",
            "password": "yourPassword"
        ]
        uploadUserWithAvatar(userData: userData, avatar: selectedImage)
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true)
    }
}
第二步:构建Multipart请求上传数据+图片

要同时传文本参数和图片,必须用multipart/form-data格式,这是HTTP上传文件的标准方式。下面是核心的上传代码:

func uploadUserWithAvatar(userData: [String: String], avatar: UIImage) {
    // 替换成你的实际接口地址
    guard let uploadURL = URL(string: "https://your-api-domain.com/api/users/create") else {
        print("无效的上传URL")
        return
    }

    var request = URLRequest(url: uploadURL)
    request.httpMethod = "POST"
    
    // 生成唯一的分隔符(boundary),用来区分不同的字段
    let boundary = UUID().uuidString
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

    // 构建请求体数据
    var requestBody = Data()

    // 先添加所有用户文本参数
    for (key, value) in userData {
        requestBody.append("--\(boundary)\r\n".data(using: .utf8)!)
        requestBody.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
        requestBody.append("\(value)\r\n".data(using: .utf8)!)
    }

    // 把图片转成Data(这里用JPEG,压缩质量0.8平衡清晰度和大小)
    guard let avatarData = avatar.jpegData(compressionQuality: 0.8) else {
        print("图片转Data失败")
        return
    }

    // 添加图片字段(注意name要和后端接口约定的一致,比如后端要求传"avatar"就写这个)
    requestBody.append("--\(boundary)\r\n".data(using: .utf8)!)
    requestBody.append("Content-Disposition: form-data; name=\"avatar\"; filename=\"user-avatar.jpg\"\r\n".data(using: .utf8)!)
    requestBody.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
    requestBody.append(avatarData)
    requestBody.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

    // 发送上传请求
    let uploadTask = URLSession.shared.uploadTask(with: request, from: requestBody) { data, response, error in
        // 回到主线程处理结果(UI操作必须在主线程)
        DispatchQueue.main.async {
            if let error = error {
                print("上传出错:\(error.localizedDescription)")
                // 这里可以给用户弹个提示,比如"上传失败,请重试"
                return
            }

            // 解析服务器返回的结果
            if let data = data, let responseStr = String(data: data, encoding: .utf8) {
                print("上传成功,服务器返回:\(responseStr)")
                // 上传成功后,比如跳转到用户主页或者显示成功提示
            } else {
                print("未收到服务器有效返回")
            }
        }
    }
    uploadTask.resume()
}
关键注意事项
  • 权限配置:必须在Info.plist里添加相机和相册的权限描述,不然App会崩溃:
    • 新增NSCameraUsageDescription,值写"需要访问相机拍摄头像"
    • 新增NSPhotoLibraryUsageDescription,值写"需要访问相册选择头像"
  • 接口字段匹配:代码里的name="avatar"要和后端接口要求的图片字段名完全一致,比如后端如果叫profile_image就改成这个
  • 图片格式:如果后端要求PNG格式,就把avatar.jpegData(...)换成avatar.pngData(),同时把Content-Type改成image/png
  • 错误处理:实际项目里可以把上传失败的情况做成弹窗提示用户,而不是只打印日志

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:02:29