How to fix CORS error on GCP storage upload when file is successfully uploaded but getting...

Discussão em 'Angular' iniciado por mg360, Novembro 25, 2024.

  1. mg360

    mg360 Guest

    Been battling with this for too long and need help.

    I am generating a resumable URL in php that I then pass to my angular app to upload a file.

    My files are successfully uploading to storage as evidenced by seeing the filename is in the storage bucket and downloadable and usable. However I was always get an error response that is CORS related.

    This is the backend PHP code that generates the URL

    public function generateResumableUploadUrl($engagementId, $fileType){
    global $logger;

    try {
    // Step 1: Decode JWT credentials
    $logger->log(LOG_LEVEL_DEBUG, 'Decoding JWT credentials for resumable upload URL', __FILE__, $engagementId);
    $credentials = $this->decodeJwt();
    if (!$credentials) {
    throw new Exception('Failed to decode JWT credentials.');
    }
    $logger->log(LOG_LEVEL_DEBUG, 'JWT credentials decoded successfully', __FILE__, $engagementId);

    // Step 2: Initialize Google Cloud Storage client
    $logger->log(LOG_LEVEL_DEBUG, 'Initializing Google Cloud Storage client', __FILE__, $engagementId);
    $storage = new StorageClient([
    'keyFile' => $credentials,
    ]);

    // Step 3: Retrieve the bucket
    $yoyo_optimiser = yoyo_optimiser::get_instance();
    $bucketName = $yoyo_optimiser->get_yoyo_storage_bucket();
    $client = $yoyo_optimiser->get_yoyo_client();
    $logger->log(LOG_LEVEL_DEBUG, 'Attempting to retrieve bucket: ' . $bucketName, __FILE__, $engagementId);
    $bucket = $storage->bucket($bucketName);
    if (!$bucket) {
    throw new Exception('Failed to access bucket: ' . $bucketName);
    }
    $logger->log(LOG_LEVEL_DEBUG, 'Bucket retrieved successfully: '. print_r($bucket,true), __FILE__, $engagementId);

    // Step 4: Create a resumable upload session
    $fileName = $client . '/staging/' . $engagementId . $fileType;
    //$logger->log(LOG_LEVEL_DEBUG, 'Creating resumable upload session for file: ' . $fileName, __FILE__, $engagementId);

    $uploader = $bucket->getStreamableUploader(fopen('php://temp', 'rb'), [
    'name' => $fileName,
    'resumable' => true,
    'contentType' => 'application/octet-stream',

    ]);


    // Generate the upload URI
    $resumableUrl = $uploader->getResumeUri();
    $logger->log(LOG_LEVEL_INFO, 'Resumable upload URL generated successfully: ' . $resumableUrl, __FILE__, $engagementId);

    return $resumableUrl;

    } catch (Exception $e) {
    // Error handling and logging
    $logger->log(LOG_LEVEL_ERROR, 'Exception encountered while generating resumable upload URL: ' . $e->getMessage(), __FILE__, $engagementId);
    $logger->log(LOG_LEVEL_ERROR, 'Error code: ' . $e->getCode(), __FILE__, $engagementId);
    $logger->log(LOG_LEVEL_ERROR, 'Stack trace: ' . $e->getTraceAsString(), __FILE__, $engagementId);

    return false;
    }
    }


    As stated this will generate a URL that looks like this https://storage.googleapis.com/uplo...ot.com/o?uploadType=resumable&upload_id=A...Q

    Note the actual bucket name is readacted and put ... in the id. However as stated the url is allowing successful upload.

    Next this is my cors policy on the bucket

    [
    {
    "origin": ["*"],
    "method": ["GET", "HEAD", "PUT", "POST", "DELETE", "OPTIONS"],
    "responseHeader": [
    "Content-Type",
    "Content-Length",
    "Content-Range",
    "Range",
    "X-Requested-With",
    "X-Goog-Resumable",
    "Authorization",
    "Origin"
    ],
    "maxAgeSeconds": 3600
    }
    ]


    Next this is the angular code

    private async uploadFileWithProgress(
    resumableUrl: string,
    fileBlob: Blob,
    startByte: number,
    totalSize: number,
    engagementId: string
    ): Promise<boolean> {
    this.logger.debug(
    `Starting upload for engagementId: ${engagementId}`,
    'VideoUploadService',
    '@4.0'
    );
    this.logger.debug(
    `Upload details: resumableUrl=${resumableUrl}, startByte=${startByte}, totalSize=${totalSize}`,
    'VideoUploadService',
    '@4.0.1'
    );

    try {
    const headers = {
    'Content-Type': fileBlob.type || 'application/octet-stream',
    'Content-Range': `bytes ${startByte}-${startByte + fileBlob.size - 1}/${totalSize}`,
    };

    this.logger.debug(
    `Headers: ${JSON.stringify(headers)}`,
    'VideoUploadService',
    '@4.0.2'
    );

    const response = await fetch(resumableUrl, {
    method: 'PUT',
    headers,
    body: fileBlob,
    });

    if (response.ok) {
    this.logger.info(
    `File uploaded successfully for engagementId: ${engagementId}`,
    'VideoUploadService',
    '@4.1'
    );

    // Update progress
    const uploadedBytes = startByte + fileBlob.size;
    const progressPercentage = (uploadedBytes / totalSize) * 100;
    this.logger.debug(
    `Upload progress: ${progressPercentage.toFixed(2)}%`,
    'VideoUploadService',
    '@4.2'
    );
    this.progressSubject.next(progressPercentage);

    await this.saveUploadProgress(engagementId, uploadedBytes, totalSize);
    return true;
    } else {
    const errorText = await response.text();
    this.logger.error(
    `File upload failed. Status: ${response.status}, Response: ${errorText}`,
    'VideoUploadService',
    '@4.3'
    );
    return false;
    }
    } catch (error: any) {
    this.logger.error(
    `Error during file upload: ${error.message || error}`,
    'VideoUploadService',
    '@5'
    );

    this.logger.debug(
    `Failed fetch details: resumableUrl=${resumableUrl}, startByte=${startByte}, totalSize=${totalSize}, fileBlob size=${fileBlob.size}`,
    'VideoUploadService',
    '@5.1'
    );
    return false;
    }
    }


    The console in the browser logs:


    [error] - Error during file upload: Failed to fetch VideoUploadService @5 and [error] - Video upload failed for engagementId: e6e32005-a066-4f30-a171-2dde3c468813 VideoUploadService @2.1
    ​

    Network request in the broswer [​IMG]

    [​IMG]

    [​IMG]

    Lastly I checked this: Unable to fix CORS policy on Google Cloud storage but could not seem to figure it out. and few others.

    Thanks in advance as I have been using trial and error and reading docs and can't work out what the problem is. Open to other solutions if there are suggestions. The objective is to support client side uploads for the public that are not-logged in users who have been given secure URL via my backend.

    Continue reading...

Compartilhe esta Página