Vue 프론트에서 S3에 파일 업로드하고, 파일 이용을 위해 S3 경로만 백엔드로 전달 / DB에 저장한 후 사용하였다

 

 

1. 초기화

import AWS from "aws-sdk"
import JSZip from 'jszip'

var albumBucketName = process.env.VUE_APP_BUCKET_NAME;
var bucketRegion = process.env.VUE_APP_BUCKET_REGION;
var IdentityPoolId = process.env.VUE_APP_IDENTITY_POOLID;

AWS.config.update({
  region: bucketRegion,
  credentials: new AWS.CognitoIdentityCredentials({
    IdentityPoolId: IdentityPoolId
  })
});

var s3 = new AWS.S3({
  apiVersion: '2006-03-01',
  params: {Bucket: albumBucketName}
});

AWS를 import한다

파일 압축이 필요해 JSZip도 import했는데, 필요 없다면 제거해도 무방하다

.env파일에서 버켓 이름, 지역, ID를 가져오고, AWS를 설정한다

 

 

2. 이미지(들) 업로드

	uploadPhoto: async (albumName, elId) => {
		var files = document.getElementById(elId).files;
		if (!files.length)return;

		var photoKeyList = [];
		var promises = [];
		
		files.forEach(async file => {
			var fileName = file.name;
			var albumPhotosKey = albumName + '/';
			var slice = fileName.split(".");
			var photoKey = albumPhotosKey + slice[0] + "_" + new Date().getTime() + "." + slice[1];
			
			const result = s3.upload({
				Key: photoKey,
				Body: file,
				ACL: 'public-read'
			}).promise().then(function () {
				photoKeyList.push(photoKey);
			})
			await promises.push(result);
		})
		
		await Promise.all(promises);
		return photoKeyList;
	},

사진이 업로드될 앨범의 이름과 사진을 받는 elements의 id  두 가지를 인자로 받는다

여러장의 사진을 한번에 올릴수 있도록, forEach를 반복하며 s3에 업로드한다

이때, 업로드는 비동기적으로 진행되므로, 빈 list를 반환하지 않도록 반복문에서는 promises에 업로드 정보를 저장하고, Promise.all로 한번에 처리한다

 

사진 이름의 중복을 고려해, 사진의 원본 이름에 현재 시간을 추가해 업로드한다

(이 경우, 서로 다른 두 사용자가 같은 이름의 사진을 milli? nano?초 단위로 동시에 업로드하면 하나가 삭제된다는 문제가 발생하긴 하는데.... 토이프로젝트 수준이라 일단 넘어갔다) 

 

완료 후에는 사진 경로들이 저장된 photoKeyList가 반환된다

 

사용 예시

<v-file-input id="imgfile" filled dense></v-file-input>
<button @click="upload"></button>

...

<script>
	method:{
    	async upload() {
			let ImgUrl = await awss3.uploadPhoto(
				"img",
				"imgfile"
			);
		}
	}
</script>

 

 

3. 압축파일 업로드

uploadZip: () => {
	var files = document.getElementById('photoupload').files;
	// make zip file
	const zip = new JSZip;
	
	files.forEach( file => {
		zip.file(file.name, file, { base64: true })	
	})
	var albumPhotosKey = 'zip/';
	var zipName = new Date().getTime() + ".zip";
	var photoKey = albumPhotosKey + zipName;
	zip.generateAsync({ type: 'blob' })
		.then(function (content) {
			s3.upload({
				Key: photoKey,
				Body: content,
				ACL: 'public-read'
			},
			function (err, data) {
				if (err) {
					return;
				}
				else {
					console.log(data);
				}
			});
		})
	return photoKey;
}

압축할 file들을 찾고, key와 name을 설정하는것까진 위와 큰 차이가 없다

 

별도의 이름이 없어 현재 시간만으로 사용하였다

(사용에는 별 문제가 없지만, 사람이 작업할 때 좋지 못하다는걸 느꼈다.. 하지만 코스트 문제로 인해 넘어갔다)

 

JSZip lib을 이용해 압축파일을 생성하였고, 생성 완료와 동시에 s3에 업로드했다

generateAsync가 이름값대로 async하게 작동하는지, 반환값이 있음에도 문제가 생기는 일은 없었다

 

 

4. 이미지, 압축파일 삭제하기

deletePhoto(photoKeys, zipKey) {
	photoKeys.forEach(photoKey => {		
		s3.deleteObject({ Key: photoKey }, function (err) {
			if (err) { return err; }
		});
	});
	if (zipKey) {
		s3.deleteObject({ Key: zipKey }, function (err) {
			if (err) { return err; }
		});
	}		
}

DB에서만 삭제하면 서비스 이용에는 문제가 없겠지만.. S3 용량에 문제가 생길것 같았다

따라서 삭제 함수를 작성해, 글이 삭제되거나 할때 같이 호출해 삭제되도록 했다

 

photoKey는 DB에서 가져온 img 이름들의 url List이며, s3에 접근해 삭제한다

zipKey는 역시 zip파일을 위한 삭제과정으로, zip이 없으면 역시 제거해도 무방하다

이번엔 반환값이 없으므로 별도의 동기 처리는 하지 않았다

 

 

5. 이미지 수정하기

updatePhoto: async (albumName, photoKey, elId) => {
	var file = document.getElementById(elId).files;
	if (file.length == 0) {
		return;
	}
	if (photoKey && photoKey != 'profileImg/noImg_1628231352109.png') {
		awss3.deletePhoto([photoKey], "");
	}
	return await awss3.uploadPhoto(albumName, elId);
},

글 수정 과정에서 기존 이미지가 다른 이미지로 바뀌는 일이 있다

이 경우 기존 이미지를 삭제하고, 새 이미지를 업로드한 후 DB저장을 위해 새로운 경로를 반환하였다

 

 

6. 이미지, 압축파일 다운로드하기

<a
	:href=" 'https://' + BUCKET_NAME + '.s3.' + BUCKET_REGION + '.amazonaws.com/' + downloadUrl"
	download
>
</a>

a태그의 href에 S3 파일 URL을 넣어주면 알아서 다운로드 된다

이미지를 띄우고 싶을때도 동일하게 img태그의 src에 넣으면 된다

 

 

7. 전체 코드

import AWS from "aws-sdk"
import JSZip from 'jszip'

var albumBucketName = process.env.VUE_APP_BUCKET_NAME;
var bucketRegion = process.env.VUE_APP_BUCKET_REGION;
var IdentityPoolId = process.env.VUE_APP_IDENTITY_POOLID;

AWS.config.update({
  region: bucketRegion,
  credentials: new AWS.CognitoIdentityCredentials({
    IdentityPoolId: IdentityPoolId
  })
});

var s3 = new AWS.S3({
  apiVersion: '2006-03-01',
  params: {Bucket: albumBucketName}
});

export const awss3 = {
	updatePhoto: async (albumName, photoKey, elId) => {
	
		var file = document.getElementById(elId).files;
		if (file.length == 0) {
			return;
		}
		if (photoKey && photoKey != 'defaultImg.png') {
			awss3.deletePhoto([photoKey], "");
		}
		return await awss3.uploadPhoto(albumName, elId);

	},

	uploadPhoto: async (albumName, elId) => {
		var files = document.getElementById(elId).files;

		if (!files.length) {
			return;
		} 

		var photoKeyList = [];

		var promises = [];
		
		files.forEach(async file => {
			var fileName = file.name;
			var albumPhotosKey = albumName + '/';

			var slice = fileName.split(".");

			var photoKey = albumPhotosKey + slice[0] + "_" + new Date().getTime() + "." + slice[1];
			
			const result = s3.upload({
				Key: photoKey,
				Body: file,
				ACL: 'public-read'
			}).promise().then(function () {
				photoKeyList.push(photoKey);
			})

			await promises.push(result);

		})
		
		await Promise.all(promises);
		
		return photoKeyList;
	},

	uploadZip: () => {
		var files = document.getElementById('photoupload').files;

		// make zip file
		const zip = new JSZip;
		
		files.forEach( file => {
			zip.file(file.name, file, { base64: true })	
		})

		var albumPhotosKey = 'album/';
		var zipName = new Date().getTime() + ".zip";
		var photoKey = albumPhotosKey + zipName;
		zip.generateAsync({ type: 'blob' })
			.then(function (content) {
				
				s3.upload({
					Key: photoKey,
					Body: content,
					ACL: 'public-read'
				},
				function (err, data) {
					if (err) {
						return;
					}
					else {
						console.log(data);
					}
				});
			})
		return photoKey;
	},
	
	deletePhoto(photoKeys, zipKey) {
		photoKeys.forEach(photoKey => {		
			s3.deleteObject({ Key: photoKey }, function (err) {
				if (err) { return err; }
			});
		});

		if (zipKey) {
			s3.deleteObject({ Key: zipKey }, function (err) {
				if (err) { return err; }
			});
		}		
	}
}

export default awss3;

 

'WEB 공부' 카테고리의 다른 글

[WEB] Vuex 동기화 문제 해결  (0) 2021.10.06
[WEB] Jenkins Permission denied 에러 해결법  (0) 2021.10.06
[WEB] Jenkins로 CI/CD 구축하기  (0) 2021.10.05
[WEB] Spring - DI  (0) 2021.05.18
[WEB] Cookie & Session  (0) 2021.05.16
[WEB] Backend - EL, JSTL  (0) 2021.05.16
[WEB] Backend - Servlet, JSP  (0) 2021.05.16

+ Recent posts