파일 업로드 (memoryStorage)
https://www.npmjs.com/package/multer
multer
Middleware for handling `multipart/form-data`.. Latest version: 1.4.4, last published: 3 months ago. Start using multer in your project by running `npm i multer`. There are 3171 other projects in the npm registry using multer.
www.npmjs.com
multer는 node.js에서 파일 업로드 / 다운로드 처리를 할 수 있는 기능을 제공해주는 모듈이다.
사용자의 파일을 서버에 업로드하고자 한다면 다음과 같은 코드를 작성할 수 있을 것이다.
import multer from 'multer';
...
const storage = multer.diskStorage({
// callback을 통해 입력 파일을 저장할 디렉터리 설정
destination(req, file, callback) {
callback(null, 'uploadedFiles/');
},
// callback을 통해 입력 파일의 파일명 설정
filename(req, file, callback) {
callback(null, `${Date.now()}__${file.originalname}`);
},
})
const upload = multer({storage:storage});
router.post("/upload", upload.array('img'), async (req, res, next)=>{
...
});
...
upload.array('img') 의 img는 파일을 받을 키값을 의미한다.
사용자가 파일을 업로드하고 콘솔에 파일을 찍으면 파일정보는 다음과 같이 나올 것이다.
{
fieldname: 'img',
originalname: 'star1.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploaded/',
filename: 'star1.png',
path: 'uploaded\\star1.png',
size: 1029
}
위의 fieldname을 보면 'img'라고 나와 있다.
클라이언트에서 서버로 파일 전송 시, 키 값이 img인 파일만 받아서 처리한다는 뜻이다.
또한 파일이 저장될 경로(destination)와 최종경로(path)까지 표시된다.
multer는 파일정보를 memory에 저장하는 것이 가능하다.
import multer from 'multer';
// diskStorage보다 코드가 더 간단하다.
const upload = multer({storage : multer.memoryStorage()});
router.post("/upload", upload.array('img'), async (req, res, next)=>{
...
})
...
memoryStorage에 보관된 파일의 정보를 출력하면 아래처럼 나온다.
{
fieldname: 'img',
originalname: 'star1.png',
encoding: '7bit',
mimetype: 'image/png',
buffer: <Buffer 89 50 4e 47 0d ... 979 more bytes>,
size: 1029
}
memoryStorage를 이용하면 파일이 저장될 경로를 입력에따라 동적 지정이 가능하다.
그러나, memoryStorage는 fs 모듈로 읽어올 수 없다.
memoryStroage로 동적 경로를 지정하기 위해서는 다음 모듈이 있어야 한다.
https://www.npmjs.com/package/streamifier
streamifier
Converts a Buffer/String into a readable stream. Latest version: 0.1.1, last published: 7 years ago. Start using streamifier in your project by running `npm i streamifier`. There are 242 other projects in the npm registry using streamifier.
www.npmjs.com
위 모듈은 createReadStream() 함수만 가지고 있다.
Q. fs 에도 위 함수가 있지 않은가?
fs 모듈에도 createReadStream을 지원하고 있다.
그러나 이 함수는 디렉터리 상의 존재하는 파일만 읽을 수 있다.
때문에, fs 모듈로는 메모리 상의 파일을 읽어올 수 없다.
아래의 코드를 살펴보자.
import fs from 'fs';
import multer from 'multer';
...
const upload = multer({storage : multer.memoryStorage()});
router.post("/upload", upload.array('img'), async (req, res, next)=>{
const file = req.files[0];
console.log(file);
const x = fs.createWriteStream("./uploaded/image.png");
fs.createReadStream(file.buffer).pipe(x);
...
});
...
위 코드에서 memoryStorage에 저장된 파일 정보를 읽어
uploaded 폴더에 image.png 형태의 파일이 생성되기를 기대한다.
당연히 사용자의 입력 파일도 image 파일이다.
그러나, 위 코드는 오류가 발생한다.
The "src" argument must be of type string or an instance of Buffer or URL
위 오류를 통해 fs에서 재공해주는 createReadStream과 createWriteStream은 파일의 경로를 명시해야 한다고 볼 수 있다.
실제로 두 함수의 파라미터를 확인해보면 path를 받고 있는 것을 알 수 있다.
fs 모듈 하나로는 memoryStorage의 파일을 받아올 수 없는 상황이다.
위 이슈는 streamifier의 createReadStream 함수가 해결해준다.
아래 코드를 살펴보자.
import multer from 'multer';
...
const upload = multer({storage : multer.memoryStorage()});
router.post("/upload", upload.array('img'), async (req, res, next)=>{
const file = req.files[0];
console.log(file);
// 파일을 쓸 경로를 명시
const stream = fs.createWriteStream("./uploaded/image.png");
// 파일 데이터를 파일을 쓸 대상에 입력
streamifier.createReadStream(file.buffer).pipe(stream);
...
});
파일을 쓰는 과정은 다음과 같다.
1. fs.createWriteStream()으로 파일을 쓸 경로를 명시한다.
2. streamifier로 파일의 buffer를 읽어들이고 pipe()에 1의 경로를 입력한다.
3. streamifier의 버퍼를 삽입하여 파일을 새로 쓴다.
위 그림처럼 성공적으로 파일이 생성됐다.
이미지 뿐만 아니라 동영상 파일도 가능하다.
(단 영상의 크기에 따라 시간이 달라진다.)