front에서 직접 이미지를 올리더라도, S3의 bucket URL 및 키를 모두 front에서 관리하는 것은 보안상 위험하므로
Backend에서 pre-signed URL을 프론트로 주면, front에서 그 URL을 이용해서 올리는 방식이 권장된다.
pre-signed URL은 설정한 시간(예: 1시간) 동안 유효하다.

java 백엔드에서 pre-signed URL을 생성하고, front에서 react를 이용해서 파일을 올리는 예제는 다음과 같다.

 



백엔드:  AWS-SDK이용.

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URL;
import java.util.Date;

@Service
public class S3Service {
    private final AmazonS3 s3Client;
    private final String bucketName;

    public S3Service(
            @Value("${aws.accessKeyId}") String accessKeyId,
            @Value("${aws.secretKey}") String secretKey,
            @Value("${aws.region}") String region,
            @Value("${aws.s3.bucketName}") String bucketName) {
        this.bucketName = bucketName;
        this.s3Client = AmazonS3ClientBuilder
                .standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKeyId, secretKey)))
                .build();
    }

    public URL generatePresignedUrl(String objectKey, HttpMethod httpMethod, long expirationInSeconds) {
        Date expirationDate = new Date(System.currentTimeMillis() + (expirationInSeconds * 1000));
        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, objectKey)
                .withMethod(httpMethod)
                .withExpiration(expirationDate);

        return s3Client.generatePresignedUrl(generatePresignedUrlRequest);
    }
}

 

 

FrontEnd:

import React, { useState } from 'react';
import axios from 'axios';

function FileUpload() {
  const [selectedFile, setSelectedFile] = useState(null);

  const handleFileChange = (event) => {
    setSelectedFile(event.target.files[0]);
  };

  const handleUpload = async () => {
    if (!selectedFile) {
      console.log('Please select a file.');
      return;
    }

    try {
      // Replace with your backend API endpoint that generates the Pre-signed URL
      const response = await axios.get('http://your-backend-api/generatePresignedUrl', {
        params: {
          objectKey: 'example.jpg', // 업로드할 객체의 키
          httpMethod: 'PUT', // 업로드할 때 사용할 HTTP 메서드 (PUT 또는 POST)
          expirationInSeconds: 3600, // URL의 유효 기간 (1시간)
        },
      });

      const { url } = response.data;

      // Use the obtained Pre-signed URL to upload the file directly to S3
      await axios.put(url, selectedFile, {
        headers: {
          'Content-Type': selectedFile.type,
        },
      });

      console.log('File uploaded successfully.');
    } catch (error) {
      console.error('Error uploading file:', error);
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
}

export default FileUpload;
Posted by yongary
,