【问题标题】:SwiftUI- server not receiving base64 stringSwiftUI-服务器未接收 base64 字符串
【发布时间】:2021-11-03 20:57:05
【问题描述】:

我要做的是拍照,将其编码为 base64,然后将其以字典的形式发送到我的服务器。当我发送字典时,有时它会使用 base64 发送,有时即使我没有进行任何更改,它也会不使用它发送。 这是相机视图的代码

struct CameraView: View {

@StateObject var camera = CameraModel()

var body: some View {
    ZStack {
        CameraPreview(camera: camera)
            .ignoresSafeArea(.all, edges: .all)
        
        VStack{
            if camera.isTaken{
                
                HStack {
                    
                    Spacer()
                    
                    Button(action: camera.retake, label: {
                        Image(systemName: "arrow.triangle.2.circlepath.camera")
                            .foregroundColor(.black)
                            .padding()
                            .background(Color.white)
                            .clipShape(Circle())
                    })
                    .padding(.trailing, 10)
                }
            }
    
            Spacer()
            
            HStack{
                if camera.isTaken{
                    Button(action: camera.sendPicData , label: {
                        Text("Save")
                            .foregroundColor(.black)
                            .fontWeight(.semibold)
                            .padding(.vertical, 10)
                            .padding(.horizontal, 10)
                            .background(Color.white)
                            .clipShape(Capsule())
                    })
                    .padding(.leading)
                    
                    Spacer()
                }
                else{
                    Button(action: camera.takePic, label: {
                        ZStack{
                            Circle()
                                .fill(Color.white)
                                .frame(width: 65, height: 65)
                            
                            Circle()
                                .stroke(Color.white, lineWidth: 2)
                                .frame(width: 75, height: 75)
                        }
                    })
                }
            }
            .frame(height: 75)
        }
    }
    .onAppear(perform: {
        camera.Check()
    })
}
}

struct CameraPreview: UIViewRepresentable{

@ObservedObject var camera: CameraModel

func makeUIView(context: Context) -> UIView {
    let view = UIView(frame: UIScreen.main.bounds)
    
    camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
    camera.preview.frame = view.frame
    
    camera.preview.videoGravity = .resizeAspectFill
    view.layer.addSublayer(camera.preview)
    
    camera.session.startRunning()
    
    return view
    
}

func updateUIView(_ uiView: UIView, context: Context) {
    
}
}

以及拍照并发送到服务器的代码

class CameraModel: NSObject, ObservableObject, AVCapturePhotoCaptureDelegate{
@Published var isTaken = false
@Published var alert = false
@Published var sendPic = false
@Published var session = AVCaptureSession()
@Published var output = AVCapturePhotoOutput()
@Published var preview: AVCaptureVideoPreviewLayer!
@Published var picData = Data(count: 0)

func Check(){
    switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .authorized:
        setUp()
        return
    case .notDetermined:
        AVCaptureDevice.requestAccess(for: .video) { (status) in
            if status{
                self.setUp()
            }
        }
    case .denied:
        self.alert.toggle()
        return
    default:
        return
    }
}

func setUp(){
    do{
        self.session.beginConfiguration()
        
        let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
        let input = try AVCaptureDeviceInput(device: device!)
        
        if self.session.canAddInput(input){
            self.session.addInput(input)
        }
        
        if self.session.canAddOutput(self.output){
            self.session.addOutput(self.output)
        }
        self.session.commitConfiguration()
    }
    catch{
        print(error.localizedDescription)
    }
}

func takePic(){
    DispatchQueue.global(qos: .background).async {
        self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
        self.session.stopRunning()
        
        DispatchQueue.main.async {
            withAnimation{self.isTaken.toggle()}
        }
        print("pic taken...")
    }
}

func retake(){
    DispatchQueue.global(qos: .background).async {
        self.session.startRunning()
        
        DispatchQueue.main.async {
            withAnimation{self.isTaken.toggle()}
        }
    }
}

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    if error != nil{
        return
    }
    
    guard let imageData = photo.fileDataRepresentation() else{return}
    self.picData = imageData
}

func sendPicData(){
    let image = UIImage(data: self.picData)
    let imageData: Data = image?.jpegData(compressionQuality: 0.1) ?? Data()
    let imageString: String = imageData.base64EncodedString()
            
    let dictionary:[String:String] = ["Dot":imageString]
    if let theJSONData = try? JSONSerialization.data(withJSONObject: dictionary, options: [.prettyPrinted]) {

        guard let sendURL = URL(string:"http://toServer") else {
            print("invalid URL")
            return
        }
        var request = URLRequest(url: sendURL)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        request.httpMethod = "POST"
        request.httpBody = theJSONData

        URLSession.shared.dataTask(with: request) {data, response, error in
            guard data != nil else {
                print ("PUT Failed: \(error?.localizedDescription ?? "Unknown Error")")
                return
            }
        }.resume()

        print(dictionary)
    }
}
}

我不知道为什么它如此不一致。这对我来说没有任何意义,如果它一次有效,为什么它不能每次都有效?请帮忙

【问题讨论】:

  • 为什么要将json数据转换为字符串再转换回数据?只需使用theJSONData
  • 唯一可能失败的地方是 UIImage(data: self.picData) 是 nil... 但是因为我们不知道 picData 是什么...
  • @LeoDabus 因为当我没有它返回数据时,它会给我一个错误,说“[String:String] 类型的值没有成员数据”。您建议将 JSONData 放在哪里,因为我注释掉了 JSONText 并更改了 let POST = theJSONData!但它给了我错误“数据类型的值没有成员数据”
  • @Larme picData is func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { if error != nil{ return } guard let imageData = photo.fileDataRepresentation() else{返回} self.picData = imageData }
  • request.httpBody = theJSONData 应该足够了。不需要theJSONTextPOST。您可以保留theJSONText 用于调试目的...

标签: json swift post base64


【解决方案1】:

你为什么要切换 isTaken 之前(它是)。

我认为这里的问题是您允许在拍照线程完成之前保存照片(显示按钮)。

func takePic(){
    DispatchQueue.global(qos: .background).async {
        self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
        self.session.stopRunning()
        
        //DispatchQueue.main.async {
        //    withAnimation{self.isTaken.toggle()}  // << IT ISNT TAKEN YET
        //}
        //print("pic taken...")  // << AGAIN IT ISNT TAKEN YET
    }
}

当 takePic 线程完成后,它会调用委托(回调):

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {

    // NOW ITS TAKEN BUT MAY HAVE ERRORS

    if error != nil{
        return  // MAYBE THIS NEED SOME BETTER FEEDBACK FOR THE USER
    }
    
    guard let imageData = photo.fileDataRepresentation() else{return}  // USER FEEDBACK??

    self.picData = imageData

    // NOW ITS TAKEN


    // I'm assuming this is running on the main thread.
    withAnimation{self.isTaken.toggle()} // SO TOGGLE isTaken
}

我在写这篇文章时没有经过测试,但我认为你对回调的工作方式感到困惑。

https://en.wikipedia.org/wiki/Callback_%28computer_programming%29

func photoOutput 这里是回调(除非我在这里遗漏了一些东西,因为我自己对 SwiftUI 很陌生)。

【讨论】:

    【解决方案2】:

    问题是委托函数func photoOutput() 不是在每次拍照时都被调用。我发现在func takePic() 中,如果我将Timer 放在self.session.stopRunning() 上,它会解决问题并每次调用委托函数,但它也会导致另一个问题,停止调用self.isTaken.toggle()。为了解决这个问题,我将Timer 放在DispatchQueue.main.async 中,就像这样

    func takePic(){
        DispatchQueue.global(qos: .background).async {
            self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
            DispatchQueue.main.async {
                Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
                    self.session.stopRunning()
                }
            }
            
            DispatchQueue.main.async {
                withAnimation{self.isTaken.toggle()}
            }
            print("pic taken...")
        }
    }
    

    我不确定这是否是解决此问题的最佳方法,但我见过很多其他人遇到相同的类似问题,并且使用此解决方案,它每次都会调用委托函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-25
      • 1970-01-01
      • 2014-07-19
      • 2017-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多