(React-Django) Получение 403 Forbidden при загрузке файла в облачное хранилище google с использованием подписанного url

Я создал подписанный url для загрузки файлов (mp3 видео файл до 1GB) со стороны клиента непосредственно на облачное хранилище. Но когда я пытаюсь загрузить файл, я получаю следующую ошибку:

<Code> SignatureDoesNotMatch </Code> <Message> Access denied. </Message> <Details> The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method. </Details>

Вот как был сгенерирован URL:

https://storage.googleapis.com/bucket-name/filename?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=something.iam.gserviceaccount.com%2xyz%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20240207T120631Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=21...

Передняя часть выполнена на react, и я сначала делаю вызов для получения signedurl, а затем загружаю файл. Код React :


      const responseForSignedUrl = await axios.get(
        `${baseUrl}/api/posts/getuploadurl/`
      );
      if (responseForSignedUrl.status !== 200) {
        throw new Error("Failed to obtain signed URL.");
      }

      const signedUrl = responseForSignedUrl.data.url;

      // Upload video file to Cloud Storage using the signed URL
      const videoFormData = new FormData();
      videoFormData.append("file", video_file);

      const uploadResponse = await axios.put(signedUrl, videoFormData, {
        headers: {
          "Content-Type": "video/mp4",
        },
        onUploadProgress: (progressEvent) => {
          const percentCompleted = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total
          );
          setProgress(percentCompleted);
        },
      });
     
      if (!uploadResponse.ok) {
        throw new Error("Failed to upload video file.");
      }

Код бэкенда для генерации подписанных url:

def generate_upload_signed_url_v4(request):
    """Generates a v4 signed URL for uploading a blob using HTTP PUT.

    Note that this method requires a service account key file. You can not use
    this if you are using Application Default Credentials from Google Compute
    Engine or from the Google Cloud SDK.
    """
    bucket_name = 'production-bucket-name'
    blob_name = 'test1'

    # storage_client = storage.Client()
    current_directory = os.path.dirname(os.path.abspath(__file__))

    # Navigate to the parent directory (folder A)
    parent_directory = os.path.dirname(current_directory)

    # Access file B within folder A
    file_b_path = os.path.join(parent_directory, "signurl.json")
    storage_client = storage.Client.from_service_account_json(file_b_path)
    bucket = storage_client.bucket(bucket_name)
    blob = bucket.blob(blob_name)
    print(storage_client)
    url = blob.generate_signed_url(
        version="v4",
        # This URL is valid for 15 minutes
        expiration=datetime.timedelta(minutes=15),
        # Allow PUT requests using this URL.
        method="PUT",
        content_type="video/mp4",
       
    )

    print("Generated PUT signed URL:")
    print(url)
    print("You can use this URL with any user agent, for example:")
    print(
        "curl -X PUT -H 'Content-Type: application/octet-stream' "
        "--upload-file my-file '{}'".format(url)
    )
    return JsonResponse({'url': url})
  • Попробовал создать новую учетную запись службы с действительными правами и затем получить ключ, не получилось. Так что уверен, что что-то не так с кодом.
  • Пробовал
  • менять типы содержимого на application/octet-stream и multipart/form-data. Я что-то упустил в заголовках запроса?

Я нашел ошибку. Использование FormData() для формирования запроса всегда отправляет запрос с типом содержимого "multipart/form-data". Поэтому даже если мы создадим подписанный url с любым другим типом содержимого, он не будет работать. Я не знаю, почему он также потерпел неудачу, когда я использовал форму-данные при подписании url. В любом случае, нам нужно просто отправить файл, только видео файл, во время отправки запроса, а не форму.

Вернуться на верх