iOS Swift: ошибка загрузки хранилища Firebase — работает только начальная загрузка файла

#ios #swift #firebase #firebase-storage

#iOS #swift #firebase #firebase-хранилище

Вопрос:

Среда: Xcode 12.0.1 (Swift 5.x) iOS 13 Firebase 6.34.0 FirebaseFirestore 1.19.0 FirebaseStorage 3.9.1 GoogleDataTransport 7.5.1 PromisesObjC 1.2.11

Проблема: я инициализирую Firebase в приложении iOS и успешно записываю данные базы данных в Cloud FireStore. Затем я загружаю связанный видеофайл в хранилище Firebase с помощью асинхронного вызова.

  1. Записи в базе данных Cloud Firestore ВСЕГДА работают.
  2. Используя индекс, сгенерированный при записи в Cloud Firestore, хранилище Firebase затем используется для загрузки видео и файла данных с именами в качестве индекса из (1) выше.

Первая загрузка первого файла всегда работает при запуске нового приложения. Вторая или любая дополнительная загрузка файла завершается ошибкой со следующей ошибкой:

ошибка загрузки видеофайлов в облачном хранилище: ошибка домена = FIRStorageErrorDomain Code =-13000 «Произошла неизвестная ошибка, пожалуйста, проверьте ответ сервера». userInfo={object=PBY7Ost7nPWD8jWWF4qG.mov, ResponseBody= Не удается завершить загрузку. Текущий размер — 1692167. Ожидаемый конечный размер — 1665242., bucket=launch-me-47860.appspot.com , data={длина = 83, байты = 0x43616e20 6e6f7420 66696e61 6c697a65 … 31363635 3234322e }, data_content_type=текст / обычный; кодировка=utf-8, NSLocalizedDescription=Произошла неизвестная ошибка, пожалуйста, проверьте ответ сервера., ResponseErrorDomain=com.google.HttpStatus, ResponseErrorCode = 400}

Я нашел похожие вопросы, опубликованные в 2016 и 2017 годах, но эта проблема с записью в Firebase выглядит иначе, поскольку начальная загрузка всегда работает, а затем при следующей попытке выполнить загрузку происходит сбой с ошибкой.

Я добавил процедуру для повторной попытки загрузки, если первая загрузка завершится неудачно. Все они терпят неудачу. Вот код загрузки:

     // MARK: Write file to Firebase Cloud Storage
    private func fbCloudFileWrite(indexName: String) {
        let fbStorage = Storage.storage()
        print("(DEBUG FB) fbCloudFileWrite: upload indexName.csv and indexName.mov")
        // now upload file to cloud FireStore
        let fbStorageRef = fbStorage.reference()
        // Create a reference to the file you want to upload
        //let LaunchMeDataRef = fbStorageRef.child("LaunchMe/"   indexName   ".csv")
        //let LaunchMeVideoRef = fbStorageRef.child("LaunchMe/"   indexName   ".mov")
        var LaunchMeDataRef = fbStorageRef.child(indexName   ".csv")
        var LaunchMeVideoRef = fbStorageRef.child(indexName   ".mov")
        // Upload the file to the path "images/rivers.jpg"
        fbWriteAttempts  = 1
        DispatchQueue.main.async {
            print("(DEBUG FB) ***** write attemp #: (self.fbWriteAttempts)")
            if let vURL = self.videoURL, let dURL = self.dataFileURL {
                let uploadVideoTask = LaunchMeVideoRef.putFile(from: vURL, metadata: nil) { metadata, err in
                    if let err = err {
                        print("(DEBUG FB) cloud storage VIDEO file upload error: (err)")
                        if self.fbWriteAttempts < 4 {
                            self.fbCloudFileWrite(indexName: indexName)
                        }
                    } else {
                        print("(DEBUG FB) video uploaded: (indexName)")
                        let uploadDataTask = LaunchMeDataRef.putFile(from: dURL, metadata: nil) { metadata, err in
                            if let err = err {
                                print("(DEBUG FB) cloud storage SENSOR file upload error: (err)")
                            } else {
                                print("(DEBUG FB) sensor data uploaded: (indexName)")
                                print("(DEBUG FB) set newRecording = false to prevent duplicates")
                                // all files successfully uploaded. Set newRecording to false
                                self.newRecording = false
                            }
                        } // close uploadDataTask
                    } // close else
                } // close let uploadVideoTask
            } // close vURL unwrap
            else {
                print("(DEBUG FB) videoURL could not be unwrapped")
            }
        } // close Dispatch.main.async
  

Комментарии:

1. Просто предположение, но это self.videoURL! подозрительно. Если формат videoURL искажен, это приведет к этой ошибке, и вам следует безопасно развернуть опции перед их использованием.

2. Джей, это отличный момент. В этом случае для воспроизведения видео на экране должен быть действительным видеоролик, поэтому я предполагаю, что он хороший. Это отличный момент, и я его безопасно разверну. Сказав это, загадка в том, что это всегда работает при первом запуске приложения, но никогда при второй записи видео.

3. Обновлен код, чтобы правильно развернуть параметры URL. Проблема не решена. Первые загрузки хранилища всегда работают, а затем последовательные приводят к ошибке, что текущий размер не соответствует ожидаемому размеру. Пример кода был обновлен, чтобы отразить правильное необязательное разворачивание.

4. Хорошие обновления. Итак, скопируйте и вставьте свой код в проект, жестко закодируйте пути к моему хранилищу Firebase и передайте в трех файлах. Все они загружены правильно. Это говорит мне о том, что у вас проблема с путем или что-то еще приводит к неполной загрузке файлов.

5. Спасибо, Джей. Ваша помощь очень ценится, так как это сводит меня с ума. Я изменил свое приложение таким образом, чтобы каждая загрузка в хранилище представляла собой новый файл с filename_1, filename_2 и т.д. Когда я переключаюсь с одного просмотра на другой без записи нового видео, оно всегда загружается. Это соответствует тому, что вы также нашли. Когда я записываю новое видео, я снова получаю сообщение об ошибке.

Ответ №1:

Как ни странно, я смог решить эту проблему, установив переменную URL в пути к локальному каталогу movie в VideoPlayerViewController вместо того, чтобы передавать URL с предыдущего контроллера во время перехода. Для меня не имеет смысла, что это было бы необходимо, за исключением, возможно, некоторого кэширования, которое происходит за кулисами.

Я знаю, что правильный URL-адрес передается, поскольку при его передаче на экране воспроизводится правильное видео, но загрузка хранилища Firebase пытается загрузить предыдущее видео (отсюда и ожидаемая ошибка размера), когда указан тот же URL, который воспроизводил правильное видео.

В контроллере добавлена новая функция, которая устанавливает URL-адрес вместо его передачи (перед выполнением загрузки хранилища Firebase):

 private func setVideoStorageURL() {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let path = "output.mov"
        videoURL = paths[0].appendingPathComponent(path)
    }
  

в то время как предыдущая реализация, которая не работала, передавала URL-адрес в контроллер:

 if let destinationVC = segue.destination as? VideoPlayerViewController {
                print("(DEBUG) Setting data to be passed to VideoPlayerViewController")
                destinationVC.newRecording = newRecording
                //now set newRecording to false if it is true
                if newRecording {
                    newRecording = false
                }
                destinationVC.audioMode = audioMode
                destinationVC.selectedDevice = selectedDevice
                destinationVC.videoURL = passFileURL