# 概要
7zipファイルのパス付ファイルの解凍処理をNestJsで行います。
dockerおよびdocker-composeを使用しますので別途必要に応じてインストールします。
ルートディレクトリにbackend
フォルダを作っておきます。
mkdir backend
# dockerファイル
docker/backend/Dockerfile
FROM node:18.16.0-slim
RUN apt-get update && apt-get install -y curl \
procps \
p7zip-full \
task-japanese \
&& apt-get -y autoremove \
&& apt-get clean
ENV LANG C.UTF-8
RUN npm i -g @nestjs/cli
WORKDIR /workspace
EXPOSE 3000
p7zip-full
は7za
コマンドを提供するパッケージです。
日本語ファイル名が化けるためlanguage packも同時にインストールtask-japanese
# docker-compose.yml
docker-compose.yml
を作成します。
version: '3.9'
services:
backend:
build:
context: .
dockerfile: ./docker/backend/Dockerfile
ports:
- "8080:3000"
volumes:
- type: bind
source: ./backend
target: /workspace
command: yarn run start:dev
# build & install
nest プロジェクトを作成
docker-compose run backend nest new .
? Which package manager would you ❤️ to use? (Use arrow keys)
❯ npm
yarn
pnpm
と聞かれるので今回はyarn
を選んでいます。
# yarn install
moduleをインストールします。
docker-compose run backend yarn install
解凍パスワードの取得を環境変数から行うので。
必要なモジュールを追加インストールします。
docker-compose run backend yarn add @nestjs/config
app.moduleに追加追加します。
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule {}
# サービスに追記
backend/src/app.service.ts
import { HttpException, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { execSync } from 'child_process';
import * as fs from 'fs';
@Injectable()
export class AppService {
constructor(private config: ConfigService) {}
getHello(): string {
return 'Hello World!';
}
async unzipUpload(file: Express.Multer.File) {
try {
const command = `7za x -o./uploads ./uploads/${
file.filename
} -p${this.config.get<string>('ZIP_PASS')}`;
const res = execSync(command);
//const fileName = file.originalname.replace('7z', 'txt');
// 日本語ファイル名が文字化けする場合は以下の処理を行う
const fileName = Buffer.from(file.originalname, 'binary')
.toString('utf-8')
.replace('7z', 'txt');
const fileText = fs.readFileSync(`./uploads/${fileName}`);
return fileText.toString();
} catch (error) {
const mask = `${error}`.replace(
`-p${this.config.get<string>('ZIP_PASS')}`,
'-p********',
);
console.error(mask);
throw new HttpException(mask, 500);
}
}
}
execSync
にて7za
コマンドを実行しパス付の7zipフォルダを解凍しています。
-o オプションで解凍先のフォルダを指定できます。
res
には7zaコマンドの実行結果が出力されます。
# コントローラに追記
FileInterceptor({dest: 'uploads/'})でアップロードファイル保存先のフォルダを指定しています。
import {
Controller,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Post()
@UseInterceptors(FileInterceptor('file', { dest: 'uploads/' }))
async uploadText(@UploadedFile() file: Express.Multer.File) {
return await this.appService.unzipUpload(file);
}
}
type
@types/multer
が無い場合はインストールしておきます。
yarn add -D @types/multer
# dockerを立ち上げて
docker-compose up
# 動作確認
テキストファイルを用意して7zipでパスワード付で圧縮します。
テスト.txt
テスト
.env
に圧縮する際に使用したパスワードを追記します。
ZIP_PASS=<password for 7zip>
curlやPostmanなどで動作確認します。
curl -X 'POST' \
'http://localhost:8080/' \
-H 'accept: */*' \
-H 'Accept-Encoding: gzip, deflate, br' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@テスト.7z'
テスト
(テキストファイルの中身)と表示されれば成功です。