В настоящее время я использую компонент с питанием от React Hook, чтобы записать свой экран и впоследствии загрузить его в Google Cloud Storage. Однако по завершении файл, созданный в Google Cloud, оказывается поврежденным.
Это суть кода в моем компоненте React, отсюда useMediaRecorder: https://github.com/wmik/use-media-recorder -
let {
error,
status,
mediaBlob,
stopRecording,
getMediaStream,
startRecording,
liveStream,
} = useMediaRecorder({
onCancelScreenShare: () => {
stopRecording();
},
onDataAvailable: (chunk) => {
// do the uploading here:
onChunk(chunk);
},
recordScreen: true,
blobOptions: { type: "video/webm;codecs=vp8,opus" },
mediaStreamConstraints: { audio: audioEnabled, video: true },
});
Когда данные становятся доступными через этот хук - он вызывает onChunk (chunk), передающий двоичный Blob этому методу, чтобы выполнить загрузку, я связываюсь с этим разделом кода для выполнения загрузки:
const onChunk = (binaryData) => {
var formData = new FormData();
formData.append("data", binaryData);
let customerApi = new CustomerVideoApi();
customerApi.uploadRecording(
videoUUID,
formData,
(res) => {},
(err) => {}
);
};
customerApi.uploadRecording выглядит так (с использованием аксиомов).
const uploadRecording = (uuid, data, fn, fnErr) => {
axios
.post(endpoint + "/stream/upload", data, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then(function (response) {
fn(response);
})
.catch(function (error) {
fnErr(error.response);
});
};
HTTP-запрос выполнен успешно, и с миром все в порядке: код на стороне сервера для загрузки основан на laravel:
// это внутри контроллера.
public function index( Request $request )
{
// Set file attributes.
$filepath = '/public/chunks/';
$file = $request->file('data');
$filename = $uuid . ".webm";
// streamupload
File::streamUpload($filepath, $filename, $file, true);
return response()->json(['uploaded' => true,'uuid'=>$uuid]);
}
// есть поставщик услуг, используемый для создания нового макроса для объекта File ::, предоставляющий средства для соответствующей обработки потока:
public function boot()
{
File::macro('streamUpload', function($path, $fileName, $file, $overWrite = true) {
$resource = fopen($file->getRealPath(), 'r+');
$storageClient = new StorageClient([
'projectId' => 'myprjectid',
'keyFilePath' => '/my/path/to/servicejson.json',
]);
$bucket = $storageClient->bucket('mybucket');
$adapter = new GoogleStorageAdapter($storageClient, $bucket);
$filesystem = new Filesystem($adapter);
return $overWrite
? $filesystem->putStream($fileName, $resource)
: $filesystem->writeStream($fileName, $resource);
});
}
Итак, повторим:
- Приложение React выделяет капли,
- сторона сервера определяет, должен ли он быть создан или добавлен в Google Cloud Storage.
- на стороне сервера успешно.
4) Видео внутри платформы Google Cloud повреждено.
Однако видеофайл в контейнере Google Cloud поврежден и не воспроизводится. Я не уверен, почему он поврежден, но пока мои догадки:
Какая-то проблема типа Dodgy Mime ... - разные браузеры, похоже, обрабатывают кодек / тип файла иначе, чем медиа-рекордер: например, Кажется, что Chrome - это x-matroska (.mkv?) - firefox снова другой ... В идеале у меня был бы контейнер .webm - обратите внимание, как я устанавливаю серверную сторону имени файла, а она не исходит от клиента. Должен ли он? Я не уверен, как заставить MediaRecorder быть конкретным mimeType - я думал, что параметр
blobOptions
должен сделать это, но изменение расширения и типа mime, похоже, практически не влияет на возникновение повреждения.Некоторая проблема во время загрузки, когда HTTP-запрос не выполняется и не завершается по порядку - например,
1 onDataAvailable completes second 2 onDataAvailable completes first 3 onDataAvailable completes third
Я как бы исключил это, потому что считаю, что куски должны быть достаточно маленькими.
Какая-то проблема с API-интерфейсами Google Cloud Storage, которые я использую, возможно, не так? Поддерживает ли облачная платформа потоковую передачу и отправляет ли эта библиотека правильные параметры для этого?
Какая-то проблема с тем, как я загружаю - должны ли заголовки axios быть составными данными формы или чем-то еще?
Это пакет, который я использую на стороне сервера: https://github.com/Superbalist/flysystem-google-cloud-storage
Может ли кто-нибудь пролить свет на то, как достичь этой цели потоковой передачи в Google Cloud без повреждения видео с медиа-рекордера? Надеюсь, в этом вопросе достаточно подробностей, чтобы понять это. Проблема, как показано на рисунке, заключается не в том, чтобы доставить файл до облака Google, а в том, что полученный файл не может быть воспроизведен в любом видеоформате.
Обновить
Я заказал свои фрагменты на стороне клиента и правильно поставил их в очередь, прежде чем позволить им добраться до сервера. Никакой разницы на выходе. Как некоторые предположили, один запрос на загрузку большого двоичного объекта работает нормально.
Пробовал использовать параметр потоковой конфигурации (из чтения исходного кода кажется, что куски должны быть определенного размера, прежде чем Google распознает их как возобновляемую загрузку
$ filesystem = новая файловая система ($ adapter, ['resumable' = ›true]);
Не знаю, как: https://cloud.google.com/storage/docs/performing-resumable-uploads - реализовано в библиотеках, которые я использую (или в самих API Google Cloud, если вообще?). Мне нужно реализовать это самому? Документация со стороны Google невелика.