ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [API] MS API 이용하여 MS Office 파일 PDF로 변환하기 3 - 파일 업로드
    IT/API 2023. 5. 18. 21:38
    728x90
    반응형

    이전 포스팅을 통해 MS Azure의 설정과 암호발급 까지 완료하였다면 이제 Azure의 API를 사용하여 MS cloud(OneDrive)에 파일을 업로드할 수 있다.

    아래 포스팅에서 다룬 Node.js 에서 API 서버만들기를 통해 실제 API 서버를 구현해 볼 수 있다.

    https://mongs-drawing.tistory.com/20

     

    [Node.js] API 서버 만들기 2 - api 호출(get,post)

    1. express 구조 파악 앞의 https://mongs-drawing.tistory.com/19 포스팅에서 설치한 express-generator를 이용하여 node.js 프레임워크를 만들었다면 아래와 같은 구조의 node.js 환경이 구축이 되어 있을 것이다. 상

    mongs-drawing.tistory.com

     

    1. 환경변수 설정

    MS Azure에 접근하기 위해서 사용 하는 모든 API는 토큰 인증이 필수로 필요하다.

    토큰을 발급받기 위해서는 tenant_id, client_id, client_secret 값이 필요한데 이는 이전 포스팅 https://mongs-drawing.tistory.com/21에서 발급받은 값을 토대로 입력할 수 있다.

    tenant_idclient_idMS Azure > AAD > 앱 등록 페이지에서 앱선택 시 바로 확인이 가능하고,

    client_secret인증서 및 암호 페이지에서 발급받은 암호의 값으로 확인이 가능하다.

     

    express로 생성한 node.js 프로젝트 디렉터리에 환경변수를 담는 파일 .env를 생성하고 위의 값들을 저장시켜 놓는다.

    OAUTH_CLIENT_ID='클라이언트 아이디'
    OAUTH_TENANT_ID='디렉터리 아이디'
    OAUTH_CLIENT_SECRET='암호 값'

     

    2. Route 설정

    express를 사용하여 정상적으로 프로젝트를 생성하였다면 root 경로에 app.js 파일이 있을 것이다. app.js node module을 로딩하고 초기 initialize해야 하는 변수나 Object를 선언하고 Router에 유입이 이루어 지는 역할을 하는 JavaScript 파일이다.아래와 같이 수정을 진행하면 된다.

    // express 모듈
    var express = require('express');
    // 파일 업로드를 위해 경로를 설정해주는 모듈
    var path = require('path'); 
    
    // Route 변수 설정
    var uploadRouter = require('./routes/upload');
    
    
    app.use(express.json());
    app.use(express.urlencoded({ extended: false }));
    app.use(express.static(path.join(__dirname, 'public')));
    
    // Route 설정
    app.use('/upload', uploadRouter);
    
    module.exports = app;

     

    3. 업로드 API 작성

    app.js를 통해 클라이언트의 요청을 routes/upload.js 로 이동시키게 되고 upload.js 파일에서 업로드 API 요청을 처리한다.

    우선 사용하는 모듈과 선언된 환경변수를 불러온다.

    const path = require('path');
    const multer = require('multer');
    const fs = require('fs');
    const request = require('request');
    const jwt_decode = require('jwt-decode');
    const axios = require('axios');
    const url = require('url');
    var express = require('express');
    var router = express.Router();
    require('dotenv').config();
    const tenantId = process.env.OAUTH_TENANT_ID  
    const clientId = process.env.OAUTH_CLIENT_ID
    const clientSecret = process.env.OAUTH_CLIENT_SECRET

     

    이후 post 요청을 통해 클라이언트가 첨부한 파일을 받아오고, MS Azure에 업로드 하기위한 토큰이 서버에 존재하지 않다면 토큰 발급 API를 우선 호출한다.

    // 클라이언트의 post 요청 처리
    router.post('/', function(req, res, next) {
        const time_now = Math.floor(Date.now()/1000);
        const now = new Date();
        // 토큰이 존재하지 않는다면 토큰 발급 API 호출
        if(global.OAUTH_TOKEN == 'undefined' || global.OAUTH_TOKEN == null || global.OAUTH_EXPIRE == 'undefined' || global.OAUTH_EXPIRE < time_now + 60){
            const params = new url.URLSearchParams({
                grant_type: 'client_credentials',
                client_id: clientId,
                scope: 'https://graph.microsoft.com/.default',
                client_secret: clientSecret
            })
            // MS Azure token을 발급하는 API로, 공식 홈페이지에 설명되어 있다.
            axios.post(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, params.toString(), {
                headers: { 'Content-Type': 'application/x-www-form-urlencoded'},
                method: 'post',
            }).then(function(response) {
            // axios 또한 ajax와 같은 비동기 처리기 때문에 토큰을 발급받기 이후 실행하기 위해 then 으로 처리한다.
                global.OAUTH_TOKEN = response.data.access_token
                global.OAUTH_EXPIRE = JSON.parse(JSON.stringify(jwt_decode(response.data.access_token).exp))
                uploadToMS();
            })
            .catch(function(error) {
                console.log(error)
            })
        } else {
        // 토큰이 이미 존재한다면 upload API 함수 호출
            uploadToMS();
        }
     }
     
     module.exports = router;

     

    마지막으로 실질적으로 MS Azure로 업로드를 처리하는 API를 정의한 uploadToMS 함수를 호출하는 코드를 작성한다.

    (위의 router.post 내부에 넣어야한다. 간단하게 마지막 중괄호 바로 위에 넣으면 제대로 동작한다.)

     

    function uploadToMS(){
        // 업로드 폴더 경로와 파일명을 변수로 설정한다. 프로젝트/publick/uploads 경로로 설정했다.
        const dir = path.resolve('','public','uploads') + '/' + time_now //__dirname + "/public/uploads/" + time_now;
        if(!fs.existsSync(dir)) fs.mkdirSync(dir);
        const storage = multer.diskStorage({
            destination: function(req, file, cb) {
                cb(null, "public/uploads/" + time_now);
            },
            filename: function (req, file, cb) {
                cb(null, time_now + path.extname(file.originalname))
            }
        });
        const upload = multer({storage:storage}).single('file')
        upload(req, res, (err) => {
        	// MS Azure API에서 사용하면 에러가 발생하는 값은 제외시킨다. 
            upload_filename = req.file.originalname.replace(/\#/g,'').replace(/\\/g,'').replace(/\\/g,'').replace(/\</g,'').replace(/\>/g,'')
            const filename = encodeURI(upload_filename);
            // 파일의 용량별로 사용되는 API가 다르다.
            // options 는 파일의 size가 4194300 보다 작을 때 사용할 수 있도록 정의한다.
            var options = {
            	// 일반적으로 계정은 "유저아이디@xxx.onmicrosoft.com" 으로 되어있는데 이때 @을 url 인코딩 처리하여 %40으로 입력한다.
                url: `https://graph.microsoft.com/v1.0/users('계정')/drive/root:/${filename}:/content`,
                method: 'PUT',
                headers: {
                    Authorization: `Bearer ${global.OAUTH_TOKEN}`
                }
            }
            // options_session 은 파일의 size가 4194300 보다 클 때 세션을 유지하며 업로드 하는 API를 사용하기 위해 정의한다.
            var options_session = {
                url: `https://graph.microsoft.com/v1.0/users('계정')/drive/root:/${filename}:/createUploadSession`,
                method: 'POST',
                headers: {
                    Authorization: `Bearer ${global.OAUTH_TOKEN}`
                }
            }
            if(req.file.size < 4194300){
            	// 용량이 적을 때
                fs.createReadStream(req.file.path)
                .pipe(request.put(options, (error, response, body) => {
                    if (response.statusCode != 200 && response.statusCode != 201) {
                    	// 업로드가 실패했을 때 응답 코드이다.
                        res.send({
                            status: response.statusCode,
                            message : 'error',
                            data: JSON.parse(response.body.toString()).error.message,
                            dateTime: now
                        });
                    } else {
                        const file_id = JSON.parse(response.body.toString()).id;
                        // 업로드가 성공하였을 때 응답 코드이다.
                        res.json({
                            status: response.statusCode,
                            message: 'success',
                            data: {
                                id: file_id,
                                token: global.OAUTH_TOKEN
                            },
                            dateTime: now
                        });
                    }
                    fs.unlinkSync(req.file.path)
                }))
            } else {
            	// 용량이 클 때
                request.put(options_session, (error, response, body) => {
                	// 에러 발생 했을 때 응답 코드이다.
                    if (error || (response.statusCode != 200 && response.statusCode != 201)) {
                        res.send({
                            status: response.statusCode,
                            message: 'error',
                            data: JSON.parse(response.body.toString()).error.message,
                            dateTime: now
                        });
                    } else {
                    	// 업로드 세션이 성공적으로 연결되었을 때 호출 코드이다.
                        const uploadUrl = JSON.parse(response.body.toString()).uploadUrl;
                        const options_large = {
                            url: uploadUrl,
                            method: 'PUT',
                            headers: {
                                'Content-Length': req.file.size,
                                'Content-Range': `bytes 0-${req.file.size-1}/${req.file.size}`
                            }
                        }
                        fs.createReadStream(req.file.path)
                        .pipe(request.put(options_large, (error_, response_, body) => {
                            if (error_ || (response_.statusCode != 200 && response_.statusCode != 201)) {
                            	// 업로드가 실패하였을 때 응답 코드이다.
                                res.send({
                                    status: response_.statusCode,
                                    message : 'error',
                                    data: JSON.parse(response_.body.toString()).error.message,
                                    dateTime: now
                                });
                            } else {
                            	// 업로드가 성공적으로 완료되었을 때 응답 코드이다.
                                const file_id = JSON.parse(response_.body.toString()).id;
                                res.json({
                                    status: response_.statusCode,
                                    message: 'success',
                                    data: {
                                        id: file_id,
                                        token: global.OAUTH_TOKEN
                                    },
                                    dateTime: now
                                });
                                fs.unlinkSync(req.file.path)
                            }
                        }))
                    }
                })
            }
        })
    }

     

    여기까지 진행하였다면 MS Azure 클라우드에 파일이 업로드 되는 것을 콘솔에서 확인 할 수 있을 것이다. 

    * MS Azure API를 사용하여 MS Azure 클라우드에 업로드 하는 것이니 만큼 MS Office 파일 업로드만 지원된다.

     

    업로드한 파일을 pdf로 변환하여 다운로드 하는 방법은 다음 포스팅에서 다루도록 하겠다.

    728x90
    반응형

    댓글

Designed by Tistory.