aws / aws-sdk-js-v3

Modularized AWS SDK for JavaScript.
Apache License 2.0
3.14k stars 580 forks source link

net::ERR_H2_OR_QUIC_REQUIRED error while uploading the data to S3 for package @aws-sdk/client-s3 from client side #6504

Open alphacoder-mp3 opened 2 months ago

alphacoder-mp3 commented 2 months ago

Checkboxes for prior research

Describe the bug

uploadissue

Regression Issue

SDK version number

"@aws-sdk/client-s3": "^3.654.0"

Which JavaScript Runtime is this issue in?

Node.js

Details of the browser/Node.js/ReactNative version

v20.14.0

Reproduction Steps

Following is the code snippet for UPLOAD FILES in S3 Bucket from client side:

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

export const uploadFileToS3 = async (
  file: File,
  accessKey: string,
  secretKey: string,
  handleProgress: (progress: number) => void
): Promise<{ src: string }> => {
  const s3 = new S3Client({
    credentials: {
      accessKeyId: accessKey,
      secretAccessKey: secretKey,
    },
    region: process.env.BUCKET_REGION,
  });

  return new Promise(async (resolve, reject) => {
    try {
      const contentType = file.type;

      // Read the file as a stream
      const reader = file.stream().getReader();
      const fileStream = createProgressStream(file, handleProgress);

      // Create the S3 upload parameters
      const params = {
        Bucket: process.env.BUCKET_NAME, 
        Key: file.name,
        Body: new Response(fileStream).body, 
        ContentType: contentType,
        ACL: "public-read",
      };

      const command = new PutObjectCommand(params);

      // Send the command to S3
      await s3.send(command);

      resolve({
        src: `https://${params.Bucket}.s3.${s3.config.region}.amazonaws.com/${params.Key}`,
      });
    } catch (error) {
      reject(error);
    }
  });
};

// Helper function to track the progress
const createProgressStream = (
  file: File,
  handleProgress: (progress: number) => void
) => {
  const fileSize = file.size;
  let uploaded = 0;
  const reader = file.stream().getReader();
  return new ReadableStream({
    async start(controller) {
      handleProgress(0); // Initially 0%
    },
    async pull(controller) {
      const chunk = await reader.read();
      if (chunk.done) {
        controller.close();
        return;
      }

      uploaded += chunk.value.length;
      const progressPercentage = Math.round((uploaded / fileSize) * 100);
      handleProgress(progressPercentage);
      controller.enqueue(chunk.value);
    },
  });
};

Observed Behavior

When the s3 upload api is getting called, it is giving this error in its status in network tab status. I was implementing this with next js. net::ERR_H2_OR_QUIC_REQUIRED

Expected Behavior

It should upload the file and return back the resolved url of S3.

Possible Solution

No response

Additional Information/Context

No response

zshzbh commented 2 months ago

Hey @alphacoder-mp3 ,

The error message net::ERR_H2_OR_QUIC_REQUIRED is an error that occurs in Google Chrome and other Chromium-based browsers when trying to access a website that requires HTTP/2 or QUIC protocols, but the browser is unable to establish a connection using those protocols.

Here's what this error means:

HTTP/2 or QUIC Required: The website you're trying to access has been configured to only accept connections using the HTTP/2 or QUIC protocols, which are newer and more efficient versions of the HTTP protocol.

Browser Incompatibility: Your browser is unable to establish a connection using either HTTP/2 or QUIC protocols due to various reasons, such as:

To resolve this issue, you can try the following steps:

  1. Update your browser: Make sure you're using the latest version of your browser, as newer versions often include better support for HTTP/2 and QUIC protocols.

  2. Check your network settings: Ensure that your firewall, proxy, or antivirus software is not blocking or interfering with the HTTP/2 or QUIC protocols.

  3. Try a different browser: If the issue persists, you can try accessing the website using a different browser, as some browsers may have better support for HTTP/2 and QUIC protocols. For example Firefox.

  4. Clear browser cache and cookies: Sometimes, clearing your browser's cache and cookies can help resolve connectivity issues.

  5. If none of these steps resolve the issue, you may need to seek further assistance from your network administrator as there could be more complex network configuration issues or server-side problems causing the net::ERR_H2_OR_QUIC_REQUIRED error.

Please let me know if the steps above could solve this issue.

Thanks! Maggie

alphacoder-mp3 commented 2 months ago

Thanks, Maggie, for the quick response.

I've already performed the first, third, and fourth steps. My Chrome version is up-to-date, and I encountered the same issue on Safari. I even downloaded Arc browser and faced the same problem there. As for network-related settings, I’m not entirely sure if anything specific could be interfering. I also tried connecting to other Wi-Fi connections and mobile hotspots, but it still didn’t work.

However, I tried using XMLHttpRequest for the upload with progress tracking instead of fetch, and it worked for this following approach for upload! Below is the code:

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const uploadImageVideoToS3 = async (
  file: File,
  accessKey: string,
  secretKey: string,
  handleProgress: (progress: number) => void
): Promise<{ src: string }> => {
  const s3Client = new S3Client({
    credentials: {
      accessKeyId: accessKey,
      secretAccessKey: secretKey,
    },
    region: process.env.NEXT_PUBLIC_BUCKET_REGION as string,
  });

  const fileName = `${Date.now()}-${file.name}`;
  const contentType = file.type;

  const putObjectParams = {
    Bucket: process.env.NEXT_PUBLIC_BUCKET_NAME as string,
    Key: fileName,
    ContentType: contentType,
  };

  try {
    // Generate a pre-signed URL for PUT operation
    const command = new PutObjectCommand(putObjectParams);
    const signedUrl = await getSignedUrl(s3Client, command, {
      expiresIn: 3600,
    });

    // Use XMLHttpRequest for upload with progress tracking
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("PUT", signedUrl);
      xhr.setRequestHeader("Content-Type", contentType);

      xhr.upload.onprogress = (event) => {
        if (event.lengthComputable) {
          const percentComplete = (event.loaded / event.total) * 100;
          handleProgress(percentComplete);
        }
      };

      xhr.onload = () => {
        if (xhr.status === 200) {
          resolve({
            src: `https://${process.env.NEXT_PUBLIC_BUCKET_NAME}.s3.${process.env.NEXT_PUBLIC_BUCKET_REGION}.amazonaws.com/${fileName}`,
          });
        } else {
          reject(new Error(`Upload failed with status ${xhr.status}`));
        }
      };

      xhr.onerror = () => {
        reject(new Error("XHR request failed"));
      };

      xhr.send(file);
    });
  } catch (error) {
    console.error("Error in uploadImageVideoToS3:", error);
    throw error;
  }
};

export { uploadImageVideoToS3 };

Thanks again for your help!