[TFilter - 개인정보 검출 프로그램을 만들어 보자(1)]
https://velog.io/@sostar0832/TFilter-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EA%B2%80%EC%B6%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%96%B4-%EB%B3%B4%EC%9E%901
이전에 작상한 글과 이어진다.
이번에는 TFilterServer를 만들어보려고 한다.
사실상 TFilter의 기능을 그대로 쓰는 것이기 때문에, 쉽게 쉽게 구현할 수 있을 것 같다.
구현 그림은 아래와 같다.
개인정보 검출 결과를 json으로 전송할 생각이었기 떄문에, 간단하게 api 서버를 구현하고 싶었다.
[FastAPI]
https://fastapi.tiangolo.com/ko
FastAPI를 보니 이거면 쉽게 구현할 수 있을 것 같았다.
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
대충 예제대로 따라하고 실행하면
좋다.
이제 파일을 입력받았을 때,
TFilter를 사용하여 개인정보 내역을 추출하고,
결과를 json으로 전송하기만 하면 된다.
## file upload test
@app.post("/filter")
def get_content_filter(file: bytes = File()):
result = "None"
UPLOAD_DIR = TFilter.config.uploadTempDir
filename = str(uuid.uuid4())
filepath = os.path.join(UPLOAD_DIR, filename)
try:
data = file
with open(filepath, "wb") as fp:
fp.write(data) # 서버 로컬 스토리지에 이미지 저장 (쓰기)
tf = TFilter.TFilter()
tf.open(filepath)
result = tf.get()
if os.path.isfile(filepath):
os.remove(filepath)
except Exception as err:
return {"message": f"There was an error uploading {file.filename}",
"errMsg": f"{err}"}
return result
코드도 나름 간단하다면 간단하게 작성했다.
post로 업로드한 파일을 TFilter.config.uploadTempDir에 저장하고
TFilter를 통해 결과를 얻는 것이다.
tempupload에 있는 파일들이 중복되지 않도록 uuid.uuid4()를 사용했고,
TFilter가 끝나면 파일을 삭제하도록 설정했다.
FastApi를 쓰면서 편했던 점은 /docs를 통해 쉽게 테스트를 할 수 있다는 것이었다.
일단 tika-server를 작동하고
테스트 파일을 선택하고 실행하면
오케~ 오케~
잘 된다. 좋다.
원래 구현하려 했던 기능은 잘 작동하니, 이번에는 파일을 post로 전송했을 때 이미지를 추출해 주는 기능을 추가하려고 한다.
생각해보니 첨부파일에 개인정보가 있다는 것을 알아도
개인정보가 텍스트에 있는지 이미지에 있는지 파악하기 어려울 것이다.
텍스트에 있으면 cirt+F 처럼 find 기능을 쓰면 되겠지만
이미지에 있으면 본문에서 이미지를 찾아서 봐야하기 때문에 불편하다.
심지어 이미지의 경우에는 숨겨져 있거나, 이미지 전체가 아닌 편집되어 일부만이 본문에 노출되는 경우가 있다. 즉, 본문을 보며 눈으로 직접 파악하기에는 꽤나 어렵다는 것이다.
뭐 아무튼 그래서 일단 TFilter에도 이미지 추출기능을 추가하기로 했다.
# tikaDir : tika-app.jar 파일이 있는 경로
tikaDir="C:/pyProject/TFilter/lib/tika-app-2.9.1.jar"
# tempDir : 이미지 temp 값이 잠깐 저장되는 위치
tempDir="C:/pyProject/TFilter/temp"
# uploadDir : TFilterServer에서 업로드 된 파일이 임시로 저장되는 위치
uploadTempDir = "C:/pyProject/TFilter/upload_temp"
일단 tika-server뿐만 아니라 tika-app을 함께 사용하기 위해 config.py 파일에 tikaDir를 추가했다.
tika-server는 이미지에서 텍스트를 추출하지만, 이미지를 추출해 저장하지는 못하기 떄문이다.
tika-app은 "tika-app.jar -z 파일명"과 같은 명령어로 쉽게 이미지를 추출할 수 있다.
def extract_img(self, imgDir):
if(self.osName == "Windows"):
self.extract_img_win(imgDir)
def extract_img_win(self, imgDir):
cmd = "java -jar \"" + config.tikaDir + "\""\
+ " -z --extract-dir=\"" + imgDir + "\""\
+ " \"" + self.filepath + "\""
subprocess.check_output(cmd, text=False)
doc_type_cmd = "java -jar \"" + config.tikaDir + "\""\
+ " -d \"" + self.filepath + "\""
doc_type = subprocess.check_output(doc_type_cmd, text=False)
if(doc_type == b"application/x-hwp-v5\r\n"):
self.extract_img_from_hwp(self.filepath, imgDir)
Tfilter에서 이미지를 추출하기 위해 extract_img 함수를 만들었다.
tika-app을 사용해 imgDir로 이미지를 추출해 내는 방식이다.
그리고 이전 글에서도 설명한 것 처럼 파일 타입이 hwp인 경우 tika-app에서 이미지를 추출하지 못하기 때문에,
extract_img_from_hwp를 사용해 추가로 이미지를 추출하도록 작성했다.
@app.post("/image")
def image_zip_download(file: bytes = File()):
result = "None"
TEMP_DIR = TFilter.config.tempDir
UPLOAD_DIR = TFilter.config.uploadTempDir
upload_filename = str(uuid.uuid4())
upload_filepath = os.path.join(UPLOAD_DIR, upload_filename)
download_filename = upload_filename
download_folder_path = UPLOAD_DIR
dowload_filepath = os.path.join(download_folder_path, download_filename)
try:
data = file
with open(upload_filepath, "wb") as fp:
fp.write(data)
tf = TFilter.TFilter()
tf.filepath = upload_filepath
temp_img_folder = os.path.join(TEMP_DIR, upload_filename)
if not os.path.exists(temp_img_folder):
os.makedirs(temp_img_folder)
tf.extract_img(temp_img_folder)
shutil.make_archive(dowload_filepath, 'zip', temp_img_folder)
if os.path.exists(temp_img_folder):
shutil.rmtree(temp_img_folder)
if os.path.exists(upload_filepath):
os.remove(upload_filepath)
result = FileResponse(path=dowload_filepath+".zip", media_type="application/zip", filename=download_filename)
except Exception as err:
return {"message": f"There was an error uploading {file.filename}",
"errMsg": f"{err}"}
return result
TfilterServer에는 코드를 위와 같이 작성했다.
TFilter를 통해 temp_img_folder로 추출한 이미지를 압축하고,
압축한 파일을 dowload_filepath로 옮겨, 리턴하는 방식이다.
한 가지 골 때리는 문제는
shutil.make_archive(dowload_filepath, 'zip', temp_img_folder)
shutil.make_archive 이 친구는 파일을 압축할 때, 압축한 파일에 ".zip"이란 이름을 추가로 붙여주는 특징이 있는데,
이미 dowload_filepath에 ".zip"이 있으면 압축된 파일이 ".zip.zip"이 되버리는 문제가 있었다.
result = FileResponse(path=dowload_filepath+".zip", media_type="application/zip", filename=download_filename)
그렇기 때문에 처음 dowload_filepath에 ".zip"을 포함하지 않고
마지막에 FileResponse에서 dowload_filepath를 불러올 떄 ".zip"을 추가해 주어야 했다.
이 사소한 문제 때문에 30분을 애먹었다 ㅎㅎ
아무튼 이번에도 테스트 파일을 넣고 실행해보면
"Download file"를 통해 파일을 다운로드 받을 수 있고
다운로드해보면 압축파일 안에 이미지가 아주 예쁘게 추출된 것을 볼 수 있다.
[GitHub - TFilter]
https://github.com/WickedFoxes/TFilter
깃허브에도 최종본을 등록해두었다.
이제 TFilterServer 구현은 완료했으니
TFboardCrawler와 TFIlterPirvacyBoard만 구현하면 된다.
으아.. 귀찮긴하지만 어떻게든 끝까지 마무리 해보자.