게시판 - 파일첨부 기능 만들기 5 (서드파티 API(Box.com) 사용 하기)

소스코드

이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.

이 게시물의 소스코드는 게시판 만들기(고급) / 게시판 - 파일첨부 기능 만들기 4 (수정/삭제)에서 이어집니다.

board.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.

git reset --hard
git pull
git reset --hard 197fafd
git reset --soft e530c6c
npm install
atom .

board.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 board.git 을 clone 합니다.

git clone https://github.com/a-mean-blogger/board.git
cd board
git reset --hard 197fafd
git reset --soft e530c6c
npm install
atom .

- Github에서 소스코드 보기: https://github.com/a-mean-blogger/board/tree/197fafd3d10cf88230d1845dadbaa38d1778b882


지금까지 첨부파일을 서버에 저장하고 불러오는 방법을 알아보았습니다. 하지만 실무에선 사용자들의 binary 데이터를 사이트가 실행되는 서버에 저장하지 않습니다. 서버는 사이트를 돌리기 위해 최적화되어야 하는데, 파일 읽기/쓰기는 서버의 성능을 떨어트리기 때문입니다.

이 문제를 가장 쉽게 해결할 수 있는 방법은, 파일들을 서버 컴퓨터와 다른 컴퓨터에 저장하는 것입니다. 서버 컴퓨터가 A라면 파일저장은 B컴퓨터에 하고 A와 B가 네트워크로 연결되어 있어서 파일을 쓰고 읽는 방법이죠. 하지만 이 방법은 서버 컴퓨터를 직접 가지고 있는 경우에만 쓸 수 있습니다. 헤로쿠(heroku)같은 호스팅 서비스를 사용하고 있다면 사용할 수 없죠. 심지어 헤로쿠를 포함한 몇몇 호스팅 서비스는 아예 파일저장이 제한됩니다. heroku 서버에 앱을 통해 업로드된 파일들은 서버가 재시작되거나 일정시간이 지나면 자동으로 지워집니다.

다른 해결법은 파일 저장공간을 제공하는 사이트를 이용하는 것입니다. 이처럼 프로젝트에 외부업체의 API나 서비스를 사용하기도 하는데, 이를 서드 파티(3rd party)를 사용한다고 표현합니다.

이번 강의에서는 이미 완성된 첨부파일 기능을 서드파티 사이트 서비스를 사용하도록 수정해 봅니다.

이 강의는 box.com을 사용하지만 일반적으로 서드파티 API를 사용할 때 어떻게 해야 하는지를 전반적으로 설명합니다. 서드파티 사용법은 어떠한 서드파티를 사용하는지에 따라 완전히 달라지기 때문에 '물고기를 잡아주는 방법'이 아니라 '물고기를 잡는 법'을 알려주는 것에 초점을 두었습니다. 강의를 읽으실 때도 각각의 과정을 잘 이해하도록 합시다.

** 이번강의는 영어의 압박이 있습니다. 모두 영어 공부합시다.

프로젝트에 맞는 서드파티 찾기

서드파티 서비스를 사용하려면 첫째로 일단 자신의 상황에 맞는 서드파티를 찾아야 하겠죠. 제가 정한 조건은

  1. 무료일 것
  2. API를 통해 파일을 업로드/다운로드 할 수 있을 것

입니다. 단순히 강의를 만들기 위한 것으로 조건을 최소한으로 하였습니다. 실무에서는 가격, 저장공간, 속도, 백업서비스 유무, A/S, 모니터링 서비스 등등 다양한 조건들을 설정해야 합니다.

구글에 'free file storage api'로 검색하면 검색결과에 사이트도 뜨고, 사이트들을 소개하는 글들도 뜹니다. 여기서 위 조건에 맞는 사이트를 찾아야 합니다.

제가 찾은 사이트는 https://box.com 입니다. 사이트 상단메뉴의 Pricing 메뉴를 눌러봅시다. 처음에는 Business Plan 탭의 서비스들이 보이는데, Individual Plan 탭을 눌러보면 무료 서비스도 있는 것을 볼 수 있습니다.

이처럼 보통 서드파티들은 자신들의 서비스와 가격을 정리해둔 pricing 페이지가 있으므로 이 페이지를 살펴보면 대략적인 조건을 확인할 수 있습니다. 인터넷 검색시에도 Amazon S3 Pricing과 같이 검색하여 서비스들의 스펙을 찾아볼 수 있습니다. (참고로 아마존의 s3 서비스(https://aws.amazon.com/s3)는 처음 12개월간만 무료라서 제외했습니다)

Box.com 가입 및 API 서비스 사용 방법

https://box.com 으로 접속하고 회원가입을 합니다. 회원가입과정은 간단하므로 생략합니다.

로그인을 하면 아래와 같이 사이트가 보입니다.

회원가입을 하고 저장공간을 받았지만, 바로 API를 사용해서 파일을 업로드/다운로드할 수 있는 것이 아닙니다. 이게 제가 처음 서드파티 배울 때 굉장이 이상하게 느껴졌던 부분이였어요. 사이트 로그인 정보를 사용해서 API들을 바로 쓸 수 있으면 좋을 텐데 API들을 사용하기 위해서는 몇가지 단계를 걸쳐야 합니다. box.com뿐만 아니라 대부분 사이트들이 다 이런 방식이므로 이 과정을 잘 이해하도록 합시다.

우선 사이트내 app을 등록해야 합니다. 여기서 app은, 우리가 만들고 있는 사이트같은 개념이 아니라, 사이트의 서비스를 사용하는 계정같은 개념입니다. 우리가 box.com의 파일 API들을 사용하려면 이 기능을 제공하는 사이트 내 app을 등록하는 것입니다. 그러면 해당 app의 id, token같은 것이 제공되고, 그 정보들을 이용해서 해당 API를 사용할 수 있는 것이죠. 일단 잘 이해가 안되더라도 그냥 저를 따라 진행해 봅시다.

좌측상단에 햄버거 버튼을 눌러 메뉴를 띄어봅시다.

Dev Console이라는 부분이 보이는데, 이처럼 API 서비스들은 사이트의 dev 페이지들이 있습니다. API가 일반사용자가 아닌 개발자들과 프로그램을 위한 것임을 생각하면 당연합니다. Dev console을 클릭합시다.

개발자 페이지로 넘어왔습니다. 이곳은 일반적인 사이트 이용자들을 위한 공간이 아니라, 이 사이트에서 제공하는 서비스를 내 프로그램에서 사용하고 싶은 개발자만을 위한 공간입니다. 어느 웹사이트든지 개발자 페이지로 넘어오면 가장 먼저 할 일은 공식 문서(사용설명서)를 찾아야합니다. 우리는 API를 사용하고자 하므로 API 문서를 읽어야 합니다. 왼쪽 메뉴에 API Docs라는 메뉴가 있죠? 이걸 누르면 공식 문서페이지로 이동합니다. 그 다음 공식문서를 봐가면서 시키는 대로 하면 됩니다.

**공식문서만 보고 직접 할 수 있는지 한번 도전해 보세요. 하다가 막히는 부분이 있으면 제 강의로 돌아와서 참고할 수도 있구요.

화면에 create new app이 보이고 이 버튼을 눌러서 app을 만듭니다. 이처럼 app은 하나가 아니라 원한다면 여러개를 만들 수도 있습니다. 각각의 app은 다른 id와 key를 가지므로 계정과 같다고 한 것입니다. create new app을 눌러봅시다.

만들 app을 선택할 수 있는데, 설명을 잘 읽어봅시다. 우리가 원하는 기능은 단순히 파일 업로드/다운로드이므로 custom app으로 충분해 보입니다. 여기에서 app이 '프로그램'의 개념이 아니고 '서비스 애드온'같은 개념임을 알 수 있습니다. 이걸 왜 헷갈리게 app이라고 부르는지 정확히 아시는 분은 댓글남겨주세요.

다음으로 app의 인증 방식의 선택입니다. box.com은 여러가지 인증방식을 제공합니다. 서드파티 사이트에 따라서 인증방법이 하나인 경우에는 이 부분이 없을 수도 있습니다.

위 세가지 옵션 중 괄호안을 살펴보면 Server Authentication과 User Authentication로 크게 나누어 지는 것을 알 수 있습니다.

  • Server Authentication 방식은 서버가 서버의 box.com계정으로 인증하고 모든 웹사이트 이용자가 그 계정의 파일 공간을 이용할 수 있습니다. 서버가 이미 box.com에 인증을 했기 때문에 웹사이트 이용자는 box.com 계정이 필요하지 않으며 box.com에 로그인할 필요가 없습니다.
  • User Authentication 방식은 box.com을 사용하는 user의 계정에 접근할 수 있게 해줍니다. 웹사이트 이용자가 자신들의 box.com 계정으로 직접 인증을 해야 하고 자신의 파일 공간만 이용할 수 있기 때문에 이 방법은 우리의 목적에 맞지 않습니다. 게시판의 파일첨부는 이용자가 올린 파일을 다른 사람들이 볼 수 있어야 하니까요.

다음으로 옵션 이름을 보면 token방식과 OAuth방식이 있는데, token방식이 더 간단하기 때문에 App Token를 선택합니다.

마지막으로 앱 이름을 지정해 주면 앱 생성이 완료되고 해당 앱의 configuration 페이지로 이동됩니다.


token인증 방식이니까 토큰을 생성해 주어야 합니다. Generate Key를 클릭해줍시다.


토큰 유효기간을 선택할 수 있는데, 저는 no expiry, 즉 무기한으로 설정하였습니다.

Client ID와 Access Token이 생성되었으면 이제 내 프로그램에서 해당 app을 사용할 준비가 되었습니다! 이제 다음에는 뭘 해야 할까요?

서드 파티 API를 좀 써보신 분들이라면 다음단계가 SDK 설치라는 것을 아시겠지요. SDKs 메뉴를 누릅시다.

SDK는 Software Development Kit의 약자로 서비스 제공자가 개발자를 위해 서비스를 미리 코딩해 둔 것입니다. 즉 모든 서비스를 함수로 만들어서 제공하기 때문에, 개발자는 그냥 라이브러리를 불러와서 사용만 하면 되는 것입니다. 우리는 현재 node js를 사용해서 개발을 하고 있으므로 Node SDK를 선택합니다.

box-node-sdk의 깃헙페이지로 이동하게 되고 스크롤을 내려보면 또 설명서가 보입니다. 이 설명서는 Box.com의 Node SDK를 위한 설명서로 node sdk를 어떻게 설치하는지, 라이브러리에 어떠한 함수들이 있는지, 각각함수들의 사용법을 설명합니다.

아래 명령어로 package를 설치합시다.

$ npm install --save box-node-sdk

그 다음으로 Getting Started 항목을 보면서 코딩을 하면 되는데..

'CLIENT_ID'는 우리가 가지고 있는데, 'CLIENT_SECRET'나 'DEVELOPER_TOKEN'는 없죠. 이상하죠. API Docs 페이지에 가서 살펴봅시다.

Box.com의 API Docs 페이지들을 뒤적거리다 보면 https://developer.box.com/guides/authentication/app-token/with-sdk/ 페이지를 볼 수 있습니다.

'CLIENT_SECRET'없이 'CLIENT_ID'와 'APP_TOKEN'만으로도 인증할 수 있음을 알 수 있습니다. 이 내용이 깃헙에도 있었으면 더 좋았을텐데..

어쨌든 다음으로 파일업로드/다운로드 기능을 수행하기 위한 설명서를 찾아야 하는데, https://github.com/box/box-node-sdk 페이지에는 해당 내용이 없죠. readme.md파일은 간단한 설명과 예제만 담고 있는데, 전체 설명서를 찾아야 합니다. Box.com의 API Docs 페이지는 API 사용 전반에 대한 설명서 이고, 우린 box-node-sdk 패키지를 사용하기 떄문에 box-node-sdk의 설명서를 찾는 것입니다.

깃헙에 Docs 폴더가 있네요. 들어가 봅시다.

docs 폴더 안에 또 여러가지 md파일들이 보이는데, 파일업로드/다운로드가 있을 것 같은 files.md를 눌러봅시다. 여기에 보면 파일 업로드와 다운로드에 대한 설명이 있습니다.

지금까지 찾은 내용을 바탕으로 이제 코드를 살펴봅시다.

폴더 구조

현재 파일을 생성하고 읽어오는 코드는 File.js에 있으므로 해당 파일의 코드가 중점적으로 변화가 있습니다. File.createNewInstance함수와 fileSchema.methods.getFileStream의 코드에 box-node-sdk의 함수를 사용하는 코드를 사용하고, 위 두 함수가 호출되는 files.js, posts.js에도 약간의 변화가 있습니다.

Package 설치

$ npm install --save box-node-sdk

코드- File 모델

먼저 전체 코드입니다. 이후 각각의 부분을 따로 살펴보겠습니다.

// models/File.js

var mongoose = require('mongoose');
var fs = require('fs');
var path = require('path');

// Box client setting
var BoxSDK = require('box-node-sdk');
var client;
var boxClientId = process.env.BOX_CLIENT_ID;
var boxAppToken = process.env.BOX_APP_TOKEN;
var isBoxEnabled = boxClientId && boxAppToken;

if(isBoxEnabled){
  var sdk = new BoxSDK({
    clientID: boxClientId,
    clientSecret: ''
  });
  client = sdk.getBasicClient(boxAppToken);
}

// schema
var fileSchema = mongoose.Schema({
  originalFileName:{type:String},
  serverFileId:{type:String},
  serverFileName:{type:String},
  size:{type:Number},
  uploadedBy:{type:mongoose.Schema.Types.ObjectId, ref:'user', required:true},
  postId:{type:mongoose.Schema.Types.ObjectId, ref:'post'},
  isDeleted:{type:Boolean, default:false},
});

// instance methods
fileSchema.methods.processDelete = function(){
  this.isDeleted = true;
  this.save();
};

fileSchema.methods.getFileStream = async function(){
  if(isBoxEnabled){
    try{ // using box.com
      var stream = await client.files.getReadStream(this.serverFileId);
    }
    catch(err){
      if(err.statusCode == 404){
        this.processDelete();
      }
      throw(err.statusCode);
    }
    return stream;
  }
  else { // using server file system
    var stream;
    var filePath = path.join(__dirname,'..','uploadedFiles',this.serverFileName);
    var fileExists = fs.existsSync(filePath);
    if(fileExists){
      stream = fs.createReadStream(filePath);
    }
    else {
      this.processDelete();
    }
    return stream;
  }
};

// model & export
var File = mongoose.model('file', fileSchema);

// model methods
File.createNewInstance = async function(file, uploadedBy, postId){
  if(isBoxEnabled){ // using box.com
    var filePath = path.join(__dirname,'..','uploadedFiles',file.filename);
    var stream = fs.createReadStream(filePath);
    var boxResponse = await client.files.uploadFile('0', `${file.filename}_${file.originalname}`, stream);
    var uploadedFile = boxResponse.entries[0];

    return await File.create({
        originalFileName:file.originalname,
        serverFileName:file.filename,
        serverFileId:uploadedFile.id,
        size:file.size,
        uploadedBy:uploadedBy,
        postId:postId,
      });
  }
  else { // using server file system
    return await File.create({
        originalFileName:file.originalname,
        serverFileName:file.filename,
        size:file.size,
        uploadedBy:uploadedBy,
        postId:postId,
      });
  }
};

module.exports = File;

box.com API를 사용하지 않고 계속해서 서버에 파일을 저장하는 방식을 사용하고 싶은 분들을 위해 파일을 local 서버에 저장하는 코드도 그대로 두었고 isBoxEnabled변수의 값에 따라 box를 사용할지 말지를 정합니다.

// Box client setting
var BoxSDK = require('box-node-sdk');
var client; // 1
var boxClientId = process.env.BOX_CLIENT_ID; // 2
var boxAppToken = process.env.BOX_APP_TOKEN; // 2
var isBoxEnabled = boxClientId && boxAppToken; // 3

if(isBoxEnabled){ // 4
  var sdk = new BoxSDK({                    //5
    clientID: boxClientId,                  //5
    clientSecret: ''                        //5
  });                                       //5
  client = sdk.getBasicClient(boxAppToken); //5
}

1. box-node-sdk의 client를 담을 변수입니다.

2. Box.com에서 생성한 client id와 app token은 보안 정보이므로 소스코드에 저장되면 안됩니다. 환경변수에 저장하여 불러오도록 합시다.

3. 환경변수에 box client id와 app token이 있는 경우, isBoxEnabledtrue로 합니다.

4. isBoxEnabledtrue인 경우에만 client를 세팅합니다.

5. https://developer.box.com/guides/authentication/app-token/with-sdk 페이지에서 본 것처럼 코드를 입력해 줍니다.

// schema
var fileSchema = mongoose.Schema({
  originalFileName:{type:String},
  serverFileId:{type:String}, // 1
  serverFileName:{type:String},
  size:{type:Number},
  uploadedBy:{type:mongoose.Schema.Types.ObjectId, ref:'user', required:true},
  postId:{type:mongoose.Schema.Types.ObjectId, ref:'post'},
  isDeleted:{type:Boolean, default:false},
});

1. Box.com API들은 파일을 읽어올 때 고유의 file id를 사용합니다.해당 정보를 저장할 수 있도록 새로운 항목을 추가하였습니다. Box.com 업로드 API를 사용해 파일을 업로드하는 경우 file id가 생성되고 그 정보를 file document의 serverFileId에 저장하고, 파일을 다운로드하는 경우 serverFileId 항목의 값을 사용하여 box.com 다운로드 API를 호출합니다.

// instance methods
fileSchema.methods.processDelete = function(){
...
fileSchema.methods.getFileStream = async function(){ // 1
  if(isBoxEnabled){
    try{ // using box.com
      var stream = await client.files.getReadStream(this.serverFileId); // 2
    }
    catch(err){
      if(err.statusCode == 404){ // 3
        this.processDelete();
      }
      throw(err.statusCode); // 4
    }
    return stream;
  }
  else { // using server file system // 5
    var stream;
    var filePath = path.join(__dirname,'..','uploadedFiles',this.serverFileName);
    var fileExists = fs.existsSync(filePath);
    if(fileExists){
      stream = fs.createReadStream(filePath);
    }
    else {
      this.processDelete();
    }
    return stream;
  }
};

1. async 키워드가 추가되었습니다.

2. https://github.com/box/box-node-sdk/blob/master/docs/files.md#download-a-file 문서의 예제와 다르게 await를 사용하여 sync로 함수를 사용하고 있습니다. await를 사용시 에러가 발생하는 경우를 관리하기 위해 try catch를 사용하였습니다.

3. 에러의 status 코드가 404인 경우에는 processDelete함수를 호출합니다.

4. 에러의 status코드를 던져줍니다. 이제 fileSchema.methods.getFileStream 함수가 에러를 던질 수 있으므로 해당 함수를 호출하는 모든 코드는 try catch를 사용해줘야 합니다.

5. isBoxEnabledfalse인 경우에는 이전과 같습니다.

// model methods
File.createNewInstance = async function(file, uploadedBy, postId){
  if(isBoxEnabled){ // using box.com
    var filePath = path.join(__dirname,'..','uploadedFiles',file.filename); // 1
    var stream = fs.createReadStream(filePath); // 2
    var boxResponse = await client.files.uploadFile('0', `${file.filename}_${file.originalname}`, stream); // 3
    var uploadedFile = boxResponse.entries[0]; // 4

    return await File.create({
        originalFileName:file.originalname,
        serverFileName:file.filename,
        serverFileId:uploadedFile.id, // 5
        size:file.size,
        uploadedBy:uploadedBy,
        postId:postId,
      });
  }
  else { // using server file system // 6
    return await File.create({
        originalFileName:file.originalname,
        serverFileName:file.filename,
        size:file.size,
        uploadedBy:uploadedBy,
        postId:postId,
      });
  }
};

1-3. 파일 위치를 만들고(1), 파일 스트림을 만들고(2), 해당 스트림으로 box.com API를 호출합니다(3).

3. 마찬가지로 await을 사용하여 sync로 만들었는데, 에러가 throw되어 나올 수 있습니다. File.createNewInstance 함수내에서 처리를 해주고 있지 않으므로 File.createNewInstance 를 호출하는 모든 코드에 try catch가 사용되어야 합니다.

4. https://github.com/box/box-node-sdk/blob/master/docs/files.md#upload-a-file 를 보면 업로드 성공시 어떠한 response가 오는지를 볼 수 있습니다. 이를 참고하여 업로드된 파일의 정보를 찾습니다.

5. Box.com의 파일id를 file document에 담습니다.

6. isBoxEnabledfalse인 경우에는 이전과 같습니다.

코드- Routers

// routes/files.js

var express  = require('express');
var router = express.Router();
var File = require('../models/File');

router.get('/:serverFileName/:originalFileName', function(req, res){
  File.findOne({serverFileName:req.params.serverFileName, originalFileName:req.params.originalFileName}, async function(err, file){ // 1
    if(err) return res.json(err);

    var stream;           // 2
    var statusCode = 200; // 2
    try{
      stream = await file.getFileStream(); // 3
    }
    catch(e){
      statusCode = e; // 4
    }

    if(stream){
      res.writeHead(statusCode, { // 5
        'Content-Type': 'application/octet-stream',
        'Content-Disposition': 'attachment; filename=' + file.originalFileName
      });
      stream.pipe(res);
    }
    else {
      res.statusCode = statusCode;  //5
      res.end();
    }
  });
});

module.exports = router;

1. file.getFileStream함수에 await을 사용하기 위해 async 키워드를 추가하였습니다.

2. file.getFileStream함수를 통해 streamstatusCode의 값이 변경될 수 있으므로 try catch 밖으로 빼줍니다.

3. file.getFileStream함수에 await를 붙여줍니다.

4. file.getFileStream함수에서 에러가 나는 경우 status 코드가 전달되므로 이 값을 statusCode에 담습니다.

5. statusCode를 response의 status 코드로 사용하게 합니다.

// create
router.post('/', util.isLoggedin, upload.single('attachment'), async function(req, res){
  var attachment;
  try{ // 1
    attachment = req.file?await File.createNewInstance(req.file, req.user._id):undefined;
  }
  catch(err){
    return res.json(err);
  }
  req.body.attachment = attachment;
  ...

// update
...
  if(post.attachment && (req.file || !req.body.attachment)){
    post.attachment.processDelete();
  }
  try{ // 1
    req.body.attachment = req.file?await File.createNewInstance(req.file, req.user._id, req.params.id):post.attachment;
  }
  catch(err){
    return res.json(err);
  }
  req.body.updatedAt = Date.now();
  ...

1. File.createNewInstance함수를 try catch로 감싸고 에러가 있는 경우 에러를 response하도록 하였습니다.

실행 결과

외부적으로는 달라진 것이 전혀 없습니다. 다만, 서버에 파일이 바로 저장되지 않고 api를 통해 box.com 클라우드에 저장되기 때문에 속도가 조금 느려집니다.

마치며...

box.com은 하나의 예제일뿐입니다. 실무에서는 다양한 서드파티 서비스를 사용할 일이 생기는데, 각 서비스마다 사용법이 다르기 때문에 결코 쉬운 일이 아닙니다. 저도 이 강의를 만들기 위해 box.com을 조사하고 사용법을 익히는데 몇시간씩 걸렸습니다. 본문에서도 말했듯이 공식 문서(설명서) 찾아 읽는 것이 가장 좋은 방법입니다. 결국 영어공부를 해야합니다.

긴 강의였는데 수고하셨습니다!

댓글

K
Kairo 2020.03.25
Ian H님 글 잘 보고있습니다! Ian H님의 글을 바탕으로 s3를 사용하고 있습니다. <a href="" download>를 이용하면 제 서버에 있는 사진일 경우 다운로드가 되지만  s3url을 이용하면 해당 url로 이동이 됩니다.  s3에서(s3가 아니더라도 url를 이용하여) 사진을 다운 받으려고 할때 어떻게 해야하는지 알려주실수 있으신가요?? blob, base64이런것들을 해줘야 하는건가요?? 감사합니다!
I
Ian H 2020.03.25
@Kairo,
해당 s3url의 response header설정때문에 그렇습니다. 위 코드를 보면 header의 'Content-Type'과 'Content-Disposition'을 설정해 주는 부분이 있는데, 이 처럼 header를 설정해 주면 해당 response는 무조건 다운로드로 됩니다.
물론 s3url의 response header는 바꿀 수 없습니다. 그러므로 s3url을 그대로 쓰지 마시고 자신의 서버에서 해당 파일을 읽어온 후 header를 override해서 response해주어야 합니다.
var http = require('http'); 
...  router.get('파일_라우트', function(req,res){   var host = 'S3_HOST_주소';   var path = '해당_파일의_S3_PATH';   var newReq = http.get({host:host,path:path}, function(s3Res) {     if(s3Res.statusCode == 200){       res.writeHead(statusCode, {         'Content-Type': 'application/octet-stream',         'Content-Disposition': 'attachment; filename=' + file.originalFileName       });       s3Res.pipe(res);     }      else{       ...     }   }).on('error', function(err) {     res.statusCode = 500;     res.end();   });   req.pipe(newReq); });
이런식으로 작성하시면 될 것같아요. (그냥 생각나는 대로 코드작성한거라 틀린부분이 있을 수도 있습니다 그냥 참고만 해주세요)
K
Kairo 2020.03.27
항상 자세히 알려주셔서 감사합니다!  Ian H님이 알려주신대로 더 해보겠습니다!!
I
Ian H 2020.03.27
@Kairo,
넵 Kairo님은 할 수 있을 거예요!
댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP