Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot get final name for export 'toBase64' @smithy/util-base64@3.0.0 in NextJS #6686

Open
4 tasks done
MinhOmega opened this issue Nov 22, 2024 · 1 comment
Open
4 tasks done
Assignees
Labels
bug This issue is a bug. p2 This is a standard priority issue potential-regression Marking this issue as a potential regression to be checked by team member response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days.

Comments

@MinhOmega
Copy link

Checkboxes for prior research

Describe the bug

I am facing an issue when integrating media uploads to S3 using presigned URLs. I have installed @aws-sdk/client-s3@3.697.0 and @aws-sdk/s3-request-presigner@3.697.0, and I am using Next.js version 14.2.3. It works fine in development mode, but when I build the application, both locally and on Vercel, it throws the following error:

./node_modules/.pnpm/@smithy+util-stream@3.3.1/node_modules/@smithy/util-stream/dist-es/index.js + 29 modules
Cannot get final name for export 'toBase64' of ./node_modules/.pnpm/@smithy+util-base64@3.0.0/node_modules/@smithy/util-base64/dist-es/index.js

To resolve this, I tried downgrading to version 3.485.0, and it is now working fine.

Regression Issue

  • Select this option if this issue appears to be a regression.

SDK version number

@aws-sdk/client-s3@3.697.0, @aws-sdk/s3-request-presigner@3.697.0

Which JavaScript Runtime is this issue in?

Node.js

Details of the browser/Node.js/ReactNative version

v20.9.0

Reproduction Steps

I have used server action to create the presigned URL then put the image with that response, here is sample code:

media.ts:

'use server'

import { getNextAuthServerSession } from '@/lib/auth'
import env from '@/lib/env'
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { nanoid } from 'nanoid'

// Initialize S3 client
const s3Client = new S3Client({
  region: env.AWS_REGION,
  credentials: {
    accessKeyId: env.AWS_ACCESS_KEY_ID,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
  },
})

export interface PresignedUrlResponse {
  uploadUrl: string
  fileKey: string
  error?: string
}

const ALLOWED_FILE_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB

export const getPresignedUrl = async (
  fileName: string,
  fileType: string,
  directory: string = 'uploads'
): Promise<PresignedUrlResponse> => {
  try {
    // Check authentication (can ignore that and hard code for example)
    const session = await getNextAuthServerSession()
    if (!session?.user?.id) {
      return { uploadUrl: '', fileKey: '', error: 'Unauthorized' }
    }

    // Validate file type
    if (!ALLOWED_FILE_TYPES.includes(fileType)) {
      return {
        uploadUrl: '',
        fileKey: '',
        error: `Invalid file type. Allowed types: ${ALLOWED_FILE_TYPES.join(', ')}`,
      }
    }

    // Clean the filename and get extension
    const cleanFileName = fileName.replace(/[^a-zA-Z0-9.-]/g, '-').toLowerCase()
    const fileExtension = cleanFileName.split('.').pop()

    if (!fileExtension) {
      return { uploadUrl: '', fileKey: '', error: 'Invalid file extension' }
    }

    // Generate a unique file key
    const uniqueId = nanoid()
    const fileKey = `${directory}/${uniqueId}-${cleanFileName}`

    // Create the presigned URL
    const putObjectCommand = new PutObjectCommand({
      Bucket: env.AWS_BUCKET_NAME,
      Key: fileKey,
      ContentType: fileType,
      // Add additional metadata
      Metadata: {
        uploadedBy: session.user.id,
        originalName: fileName,
      },
    })

    const uploadUrl = await getSignedUrl(s3Client, putObjectCommand, {
      expiresIn: 60 * 5, // URL expires in 5 minutes
      signableHeaders: new Set(['content-type']), // Only sign necessary headers
    })

    return {
      uploadUrl,
      fileKey,
    }
  } catch (error) {
    console.error('Error generating presigned URL:', error)
    return {
      uploadUrl: '',
      fileKey: '',
      error: 'Failed to generate upload URL',
    }
  }
}

export const getPublicUrl = async (fileKey: string): Promise<string> => {
  if (!fileKey) return ''

  // Clean the file key to prevent directory traversal
  const cleanFileKey = fileKey.replace(/^\/+/, '').replace(/\.{2,}/g, '.')
  return `${env.AWS_CLOUDFRONT_URL}/${cleanFileKey}`
}

// Helper function to validate file size
export const validateFileSize = async (size: number): Promise<boolean> => {
  return size <= MAX_FILE_SIZE
}

// Helper function to validate file type
export const validateFileType = async (type: string): Promise<boolean> => {
  return ALLOWED_FILE_TYPES.includes(type)
}

in the upload in client side:

// Get presigned URL for the upload
  const { uploadUrl, fileKey, error } = await getPresignedUrl(file.name, file.type, 'slider-images')
  
  if (error || !uploadUrl) {
    console.error('Failed to get upload URL')
    continue
  }
  
  // Upload the file directly to S3
  const uploadResponse = await fetch(uploadUrl, {
    method: 'PUT',
    body: file,
    headers: {
      'Content-Type': file.type,
    },
    mode: 'cors',
  })

Observed Behavior

./node_modules/.pnpm/@smithy+util-stream@3.3.1/node_modules/@smithy/util-stream/dist-es/index.js + 29 modules
Cannot get final name for export 'toBase64' of ./node_modules/.pnpm/@smithy+util-base64@3.0.0/node_modules/@smithy/util-base64/dist-es/index.js

Expected Behavior

Build the project success without any error same as version 3.485.0

Possible Solution

No response

Additional Information/Context

No response

@MinhOmega MinhOmega added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Nov 22, 2024
@github-actions github-actions bot added the potential-regression Marking this issue as a potential regression to be checked by team member label Nov 22, 2024
@zshzbh
Copy link
Contributor

zshzbh commented Nov 22, 2024

Hey @MinhOmega ,

When you see the error locally but not in the dev environment, it's typically due to differences in module resolution and environment configurations.

Could you please try to clear your local cache and try again?

# Clear caches
rm -rf .next
rm -rf node_modules/.cache

# Remove node_modules
rm -rf node_modules

# Clear package manager cache
npm cache clean --force
# or
yarn cache clean
# or
pnpm store prune

# Reinstall dependencies
npm install


Let us know if you are still facing this issue.

Thanks!
Maggie

@zshzbh zshzbh self-assigned this Nov 22, 2024
@zshzbh zshzbh added p2 This is a standard priority issue response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days. and removed needs-triage This issue or PR still needs to be triaged. labels Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a bug. p2 This is a standard priority issue potential-regression Marking this issue as a potential regression to be checked by team member response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days.
Projects
None yet
Development

No branches or pull requests

2 participants