[Electron] 5.레시피 압축 파일 생성 및 바이너리화

개요

검색 결과 페이지에서 레시피의 다운로드 버튼을 클릭하면 받을 수 있는 압축 파일을 만들 것이다. 압축 파일에는 레시피 이미지 파일과 레시피 title, ingredients, instructions 가 포함된 .txt 파일을 포함한다. 이 압축 파일은 이미지 image_file 컬럼 처럼 recipe_zip_file 이라는 새 컬럼을 만들어 바이너리 데이터로 DB에 삽입한다.

DB 컬럼 추가 및 데이터 삭제

-- 값이 누락된 데이터 삭제
DELETE *
FROM tb_recipe
WHERE title = ''
	OR ingredients = '[]'
	OR instructions = ''
	OR image_name = '';

-- 손상된 이미지 파일의 레코드 삭제 (파일은 존재하지만 손상된 파일)
DELETE
FROM tb_recipe
WHERE id = 9894;

먼저 recipe_zip_file 이라는 BLOB 타입의 컬럼을 추가한다. 이후 image_file 과 같이 그냥 로직을 실행시키면, 에러가 발생하기 때문에 알맞지 않은 DB 데이터를 한 번 삭제한다. 

압축 파일 생성 및 DB 삽입

//compress_binary.js
const path = require('path');
const fs = require('fs');
const archiver = require('archiver');

export default class CompressBinary {
    db = null;

    constructor(db) {
        this.db = db;
    }
    
    async InsertBinaryToSqliteDB() {
        const maxId = this.db.selectMaxId()[0].max_id;
        console.log('maxId: ' + maxId);

        let id = 0;
        while(id <= maxId) {
            let recipe = this.db.selectRecipeById(id)[0];

            if (recipe === undefined) {
                console.log('This is undefined.');
                id++;
                continue;
            }

            let title = recipe.title;

            let ingredients = recipe.ingredients.replace(/""/g, '"');
            let matchArray = ingredients.match(/'[^']*'|"[^"]*"/g);
            let ingredientsArray = matchArray.map(match => match.slice(1, -1));  

            let instructions = recipe.instructions;
            let imageName = recipe.image_name;
    
            let txtString = `Title: ${title} `;
            txtString += `\\n\\nIngredients: `;
    
            ingredientsArray.forEach(function(ingredient) {
                txtString += `\\n* ${ingredient}`;
            });
    
            txtString += `\\n\\nInstrunctions: \\n${instructions}`;
    
            let txtData = Buffer.from(txtString, 'utf-8');
    
            let imagePath = 'D://db/recipe db/Food Images/' + imageName + '.jpg';
            let zipBuffer;
    
            let archive = archiver('zip', { zlib: { level: 9 } });
            
            archive.append(txtData, { name: title + '_recipe.txt' });            
            archive.file(imagePath, { name: imageName + '.jpg' });
            await this.finalizeArchive(archive);
            zipBuffer = archive.read();
    
            this.db.updateRecipeZipFileById(zipBuffer, id);
            console.log(title + ' is completed.');
            
            /*
            let zipPath = 'D://db/recipe db/recipe zip/'+title+'.zip';
            fs.writeFile(zipPath, zipBuffer, 'binary', function(err) {
                if (err) {
                    console.log('Fail !! ');
                } else {
                    console.log('File written successfully: ' + zipPath);
                }
            });
            */

            id++;
        }
    }

    async finalizeArchive(archive) {
        return new Promise((resolve, reject) => {
            archive.on('finish', resolve);
            archive.on('error', reject);
            archive.finalize();
        });
    }
}
//main.js
.
.
.
import RcpSqliteDB from "../rcp_sqlite_db";
import CompressBinary from "./binary/compress_binary";
.
.
.
const createWindow = () => {
  .
  .
  .
  mainWindow.once('ready-to-show', () => {
    .
    .
    .

    //db open
    let db = new RcpSqliteDB();
    db.open();
    .
	  .
		.
		let compressBinary = new CompressBinary(db);
    compressBinary.InsertBinaryToSqliteDB();

  });
  .
  .
  .
 };

이후 CompressBinary 클래스를 작성하여 ImageBinary 클래스와 유사하게 사용한다.

  • MaxId를 조회하고 그 수 만큼 반복시킨다.
  • id로 레코드를 조회하여 각 컬럼 값을 알맞게 파싱하여 문자열로 만든 후 Buffer 타입으로 변환시켜 저장한다. (.txt)
  • image_name 컬럼 값으로 이미지 파일의 경로를 구한다.
  • 압축 파일이 될 archiver를 선언하고, 문자열 buffer 값(txtData)과 이미지 파일을 알맞은 이름을 압축 파일(archiver)에 추가한다.
  • 압축 파일을 buffer 타입으로 저장한다.
  • 변환된 binary 데이터는 DB의 BLOB 컬럼에 저장된다.