Java
Dropzone을 이용한 파일 업로드 및 AWS S3 스토리지 저장 (작성중)
Shonir00ng
2023. 2. 20. 10:42
네이버 클라우드 플랫폼의 Bucket Management에 파일 서버를 운영하려고 함!
AWSFileUtil.java
이미지/동영상 업로드 후 썸네일 생성해서 업로드 하는 것까지 넣음
package com.yido.clubd.common.utils;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.jcodec.api.FrameGrab;
import org.jcodec.api.JCodecException;
import org.jcodec.common.model.Picture;
import org.jcodec.scale.AWTUtil;
import org.springframework.web.multipart.MultipartFile;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest;
import com.amazonaws.services.s3.model.CompleteMultipartUploadResult;
import com.amazonaws.services.s3.model.GroupGrantee;
import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PartETag;
import com.amazonaws.services.s3.model.Permission;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.amazonaws.services.s3.model.UploadPartRequest;
import com.amazonaws.services.s3.model.UploadPartResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AWSFileUtil {
final static String endPoint = "https://kr.object.ncloudstorage.com";
final static String regionName = "kr-standard";
static String accessKey = Globals.accessKey;
static String secretKey = Globals.secretKey;
static String bucketName = Globals.bucketName;
// 썸네일 기본 너비
private static final int IMG_WIDTH = 120;
// 썸네일 기본 형식
private static final String IMAGE_FORMAT = "png";
// 동영상 썸네일 프레임
private static final int FRAME_NUMBER = 200;
// S3 client
final static AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, regionName))
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.build();
/**
* 파일 업로드
*
* @param folderName 업로드 경로
* @param objectName 업로드 경로 + 파일명
* @param extName 확장자명
* @param filePath 기존 파일경로
* @return
* @throws IOException
* @throws JCodecException
*/
public static void uploadFile(String folderName, String fileName, MultipartFile multipartFile) throws IOException, JCodecException {
String contentType = multipartFile.getContentType();
// 1) create folder
createFolder(folderName);
// 2) upload local file
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
objectMetadata.setContentType(contentType);
String objectName = folderName + fileName;
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, multipartFile.getInputStream(), objectMetadata);
try {
s3.putObject(putObjectRequest);
System.out.format("Object %s has been created.\n", objectName);
setObjectACL(objectName);
if(multipartFile.getContentType() != null) {
// 썸네일 생성
if (contentType.contains("image/")) {
getImgThumb(folderName, fileName, multipartFile);
} else if (contentType.contains("video/")) {
getVideoThumb(folderName, fileName, multipartFile);
}
}
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
}
/**
* 폴더 생성
*
* @param folderName 업로드 경로
*/
private static void createFolder(String folderName) {
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(0L);
objectMetadata.setContentType("application/x-directory");
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, folderName, new ByteArrayInputStream(new byte[0]), objectMetadata);
try {
s3.putObject(putObjectRequest);
System.out.format("Folder %s has been created.\n", folderName);
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
}
// 파일 다운로드
public static void downloadFile(String objectName, String downloadFilePath) throws IOException {
try {
S3Object s3Object = s3.getObject(bucketName, objectName);
S3ObjectInputStream s3ObjectInputStream = s3Object.getObjectContent();
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(downloadFilePath));
byte[] bytesArray = new byte[4096];
int bytesRead = -1;
while ((bytesRead = s3ObjectInputStream.read(bytesArray)) != -1) {
outputStream.write(bytesArray, 0, bytesRead);
}
outputStream.close();
s3ObjectInputStream.close();
System.out.format("Object %s has been downloaded.\n", objectName);
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
}
/**
* 파일 삭제
*
* @param objectName 업로드 경로 + 파일명
* @return
*/
public static void deleteFile(String objectName) {
StringBuffer str = new StringBuffer(objectName);
str.insert(objectName.lastIndexOf("/"), "/thumb");
String newObjectName = str.toString().substring(0, str.lastIndexOf(".") + 1).concat(IMAGE_FORMAT);
try {
s3.deleteObject(bucketName, newObjectName);
System.out.format("Object %s has been deleted.\n", newObjectName);
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
}
/**
* 썸네일 파일 삭제
*
* @param objectName 업로드 경로 + 파일명
* @return
*/
public static void deleteThumbnail(String objectName) {
try {
s3.deleteObject(bucketName, objectName);
System.out.format("Object(thumbnail) %s has been deleted.\n", objectName);
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
}
/**
* 이미지 썸네일 생성 (로컬)
*
* @param objectName 업로드 경로
* @param fileName 파일명
* @param filePath 기존 파일 경로
* @return
*/
public static void getImgThumb(String folderName, String fileName, MultipartFile multipartFile) throws IOException {
/*
// 기존 이미지 로컬 파일
File srcFile = new File(filePath);
// 생성할 임시 썸네일 로컬 경로
StringBuffer str = new StringBuffer(fileName);
str.insert(fileName.lastIndexOf("."), "_thumb");
String thumbFilePath = folderName + str.toString();
*/
// MultipartFile -> File
File file = new File(multipartFile.getOriginalFilename());
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
fos.write(multipartFile.getBytes());
fos.close();
// 업로드할 경로
String thumbObjectName = folderName + "thumb/" + fileName.substring(0, fileName.lastIndexOf(".") + 1).concat(IMAGE_FORMAT);
Image src = ImageIO.read(file);
float ratio = src.getWidth(null) / IMG_WIDTH;
int newHeight = (int)(src.getHeight(null) / ratio);
BufferedImage resizeImage = new BufferedImage(IMG_WIDTH, newHeight, BufferedImage.TYPE_INT_RGB);
resizeImage.getGraphics().drawImage(src.getScaledInstance(IMG_WIDTH, newHeight, Image.SCALE_SMOOTH), 0, 0, null);
// BufferedImage -> Input Stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(resizeImage, IMAGE_FORMAT, baos);
InputStream is = new ByteArrayInputStream(baos.toByteArray());
// 썸네일 업로드
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(baos.size());
objectMetadata.setContentType("image/" + IMAGE_FORMAT);
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, thumbObjectName, is, objectMetadata);
try {
s3.putObject(putObjectRequest);
setObjectACL(thumbObjectName);
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
/*
FileOutputStream out = new FileOutputStream(thumbFilePath);
ImageIO.write(resizeImage, IMAGE_FORMAT, out);
out.close();
File thumbFile = new File(thumbFilePath);
uploadThumb(uploadFilePath, thumbFile);
*/
}
/**
* 동영상 썸네일 생성 (로컬)
*
* @param objectName 업로드 경로
* @param fileName 파일명
* @param filePath 기존 파일경로
* @return
*/
public static void getVideoThumb(String folderName, String fileName, MultipartFile multipartFile) throws IOException, JCodecException {
// MultipartFile -> File
File file = new File(multipartFile.getOriginalFilename());
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
fos.write(multipartFile.getBytes());
fos.close();
// 업로드할 경로
String thumbObjectName = folderName + "thumb/" + fileName.substring(0, fileName.lastIndexOf(".") + 1).concat(IMAGE_FORMAT);
Picture pic = FrameGrab.getFrameFromFile(file, FRAME_NUMBER);
float ratio = pic.getWidth() / IMG_WIDTH;
int newHeight = (int)(pic.getHeight() / ratio);
BufferedImage bufferedImage = AWTUtil.toBufferedImage(pic);
BufferedImage resizeImage = new BufferedImage(IMG_WIDTH, newHeight, BufferedImage.TYPE_INT_RGB);
resizeImage.getGraphics().drawImage(bufferedImage.getScaledInstance(IMG_WIDTH, newHeight, Image.SCALE_SMOOTH), 0, 0, null);
// BufferedImage -> Input Stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(resizeImage, IMAGE_FORMAT, baos);
InputStream is = new ByteArrayInputStream(baos.toByteArray());
// 썸네일 업로드
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(baos.size());
objectMetadata.setContentType("image/" + IMAGE_FORMAT);
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, thumbObjectName, is, objectMetadata);
try {
s3.putObject(putObjectRequest);
setObjectACL(thumbObjectName);
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
}
// ACL 줘야 공개처리됨....
public static void setObjectACL (String objectName) {
try {
// get the current ACL
AccessControlList accessControlList = s3.getObjectAcl(bucketName, objectName);
// add read permission to user by ID
accessControlList.grantPermission(GroupGrantee.AllUsers, Permission.Read);
s3.setObjectAcl(bucketName, objectName, accessControlList);
System.out.format("Object %s has been granted permission.\n", objectName);
} catch (AmazonS3Exception e) {
e.printStackTrace();
} catch(SdkClientException e) {
e.printStackTrace();
}
}
}
jsp
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("div#file-dropzone", {
autoProcessQueue : false
, url : "/uploadImg"
, maxFiles : 5
, maxFilesize : 10 // MB 드롭다운 시 파일 크기
, resizeQuality : 1
, resizeWidth : 960
, parallelUploads : 100
, dictFileTooBig : "({{filesize}}MB)\n{{maxFilesize}}MB까지 가능합니다."
, paramName: "file"
, addRemoveLinks : true
, acceptedFiles : "image/*"
, uploadMultiple :true
, init : function() {
// 파일 개수 초과
this.on("maxfilesexceeded", function (file) {
this.removeAllFiles();
this.addFile(file);
});
// 에러 발생 (ex 파일 용량 초과)
this.on("error", function(file, message) {
alertModal.fail(message);
this.removeFile(file);
});
// 파일 dropzone area에 올라간 후 (총 업로드 개수 제한)
this.on("addedfile", function (file) {
if($(".img-area").length >= 10) {
alertModal.fail('10개까지 등록 가능합니다.');
this.removeFile(file);
}
})
// 파일 업로드 중
this.on('sending', function(file, xhr, formData){
formData.append('msNum', $("#msNum").val());
});
// 사진 업로드 완료 후
this.on("success", function(file, res){
this.removeAllFiles();
alertModal.success('업로드 완료');
goAfterModal();
});
/* this.on("successmultiple", function(file){
this.removeAllFiles()
alertModal.success('업로드 완료');
goAfterModal();
}); */
}
});
// 업로드 실행
myDropzone.processQueue();
Controller 혹은 Service
※ putObject(PutObjectRequest putObjectRequest)를 사용하면서 임시파일 생성 부분은 후에 삭제함
@RequestMapping("/uploadImg")
@ResponseBody
public void uploadImg(Map<String, Object> params, MultipartHttpServletRequest mreq) throws IllegalStateException, IOException {
Iterator<String> iter = mreq.getFileNames();
while(iter.hasNext()) {
// 다음 file[n] 값을 Multipartfile 객체로 생성
MultipartFile mFile = mreq.getFile(iter.next());
// mFile의 파일이름 가져옴
String orgFileNm = mFile.getOriginalFilename();
String extNm = orgFileNm.substring(orgFileNm.lastIndexOf(".") + 1, orgFileNm.length()).toLowerCase();
File sameFile = new File(orgFileNm); // 똑같은 이름의 파일 객체 생성 (file_name.jpg)
String filePath = sameFile.getAbsolutePath(); // 실행 중인 working directory + File에 전달한 경로값 (C:\folder_name\file_name.jpg)
File tmpFile = new File(filePath); // 절대경로로 다시 파일 객체 생성
mFile.transferTo(tmpFile); // 임시파일 객체에 mFile을 복사하면 해당 경로에 파일이 만들어짐
Path srcPath = Paths.get(filePath); // String을 Path 객체로 만들어줌
String mimeType = Files.probeContentType(srcPath); // 파일 경로에 있는 Content-Type(파일 유형) 확인
mimeType = (mimeType == null ? "" : mimeType); // 확장자가 없는 경우 null을 반환
String newFileNm = System.currentTimeMillis() + "." + extNm;
String newFolderNm = (String)params.get("msImgData"); // 업로드 할 경로
AWSFileUtil.uploadFile(folderNm, newFileNm, mFile); // 생성할 폴더명, 새 파일 이름, 복사될 파일 경로
// 업로드 후 임시파일 삭제
if(tmpFile.exists()) tmpFile.delete();
// DB insert
params.put("msImgName", newFileNm);
memberMapper.insertPicture(params);
}
}