- 1
- 이니스프리
- 조회 수 23917
안녕하세요? ^-^
이번 주말에는 비가 많이 오네요~
다들 비 피해 없으시기를 기원할게요!
저번에 '소스 공유' 게시판에 작성했던 'Python으로 구현한 그누보드 자동 글쓰기 함수'( https://studyforus.com/share/808613 )를 보완하여
멀티 파일 업로드까지 가능하도록 업데이트 하였습니다 ^^
그누보드 PHP 내장함수를 사용할 수 없기 때문에 해당 함수를 파이썬으로 변환하여 적절히 처리했구요~
다만 replace_filename() 함수에서 SHA1 방식 암호화를 하는 부분 중에 사용자 IP는 제외했습니다.
참고로 pymysql과 ftputil을 제외하면 Anaconda에 포함된 기본 모듈만 사용했어요 :)
아래 스크립트는 외부에서 그누보드 DB 및 FTP에 접속하는 것을 전제로 구현되었기 때문에
호스팅 또는 VPS에서 DB와 FTP에 외부접속할 수 있도록 설정하셔야 정상적으로 작동합니다.
import pymysql, ftputil, hashlib, os, sys from datetime import datetime from PIL import Image def file_type(x): # 그누보드의 bf_type 값을 반환하는 함수입니다. (디폴트 : 0) return {'gif' : '1', 'jpeg' : '2', 'jpg' : '2', 'png' : '3', 'swf' : '4', 'psd' : '5', 'bmp' : '6', 'tif' : '7', 'tiff' : '7', 'jpc' : '9', 'jp2' : '10', 'jpx' : '11', 'jb2' : '12', 'swc' : '13', 'iff' : '14', 'wbmp' : '15', 'xbm' : '16'}.get(x.lower(), '0') def file_upload(filename, bf_file): # FTP를 이용하여 파일을 업로드하는 함수입니다. with ftputil.FTPHost('URL을입력하세요', 'FTP계정명을입력하세요', 'FTP비번을입력하세요') as fh: fh.chdir('업로드할디렉토리를입력하세요ex./web/data/file/board') fh.upload(filename, bf_file, callback = None) return def get_filename(filename): # 파일명을 변환하는 함수입니다. ms = datetime.now().microsecond encoded_name = filename.encode('utf-8') result = f'{ms}_{hashlib.sha1(encoded_name).hexdigest()}' return result def board_write(board, subject, content, mb_id, nickname, file_list = None): # MySQL connection 및 cursor 생성 conn = pymysql.connect(host = '연결할URL을입력하세요', user = 'MySQL유저명을입력하세요', password = 'MySQL비번을입력하세요', db = 'MySQL디비명을입력하세요', charset = 'utf8') curs = conn.cursor() # 작성글 INSERT sql = f"select wr_num from g5_write_{board}" curs.execute(sql) wr_num = str(int(curs.fetchone()[0]) - 1) print(wr_num) now = datetime.today().strftime('%Y-%m-%d %H:%M:%S') # 그누보드의 날짜 형식 준수 (ex: 2021-04-05 23:45:15) sql = f"insert into g5_write_{board} set wr_num = {wr_num}, \ wr_reply = '', wr_comment = 0, ca_name = '', wr_option = 'html1', wr_subject = '{subject}', \ wr_content = '{content}', wr_link1 = '', wr_link2 = '', \ wr_link1_hit = 0, wr_link2_hit = 0, wr_hit = 1, wr_good = 0, wr_nogood = 0, \ mb_id = '{mb_id}', wr_password = '', wr_name = '{nickname}', wr_email = '', wr_homepage = '', \ wr_datetime = '{now}', wr_last = '{now}', wr_ip = '111.111.111.111', \ wr_1 = '', wr_2 = '', wr_3 = '', wr_4 = '', wr_5 = '', \ wr_6 = '', wr_7 = '', wr_8 = '', wr_9 = '', wr_10 = '', \ wr_comment_reply = '', wr_facebook_user = '', wr_twitter_user = '', \ as_re_name = '', as_tag = '', as_map = '', as_icon = '', as_thumb = '', as_video = ''" curs.execute(sql) # 부모 아이디에 UPDATE sql = f"select wr_id from g5_write_{board}" curs.execute(sql) wr_id = str(curs.fetchall()[-1][0]) print(f"wr_id : {wr_id}") sql = f"update g5_write_{board} set wr_parent = {wr_id} where wr_id = {wr_id}" curs.execute(sql) # 새글 INSERT sql = f"insert into g5_board_new ( bo_table, wr_id, wr_parent, bn_datetime, mb_id ) values \ ( '{board}', '{wr_id}', '{wr_id}', '{now}', '{mb_id}' )" curs.execute(sql) # 게시글 1 증가 sql = f"select bo_count_write from g5_board where bo_table = '{board}'" curs.execute(sql) bo_count_write = str(int(curs.fetchone()[0])) print(curs.fetchall()) print(bo_count_write) sql = f"update g5_board set bo_count_write = {bo_count_write} + 1 where bo_table = '{board}'" curs.execute(sql) # 파일 업로드 및 관련 정보를 테이블에 저장 if not file_list or file_list == [''] or file_list == []: # 첨부파일이 없는 경우에는 스크립트를 중단합니다. conn.close() sys.exit() file_count = len(file_list) for cnt, file in enumerate(file_list): ext = os.path.splitext(file)[1].lstrip('.') bf_file = f'{get_filename(file)}.{ext}' file_upload(file, bf_file) type = file_type(ext) if type != '0': # 이미지 파일의 경우 가로 및 세로를 구하고, 그 외의 경우에는 0을 대입합니다. im = Image.open(file) width, height = im.size else: width, height = 0, 0 size = os.path.getsize(file) sql = f"insert into g5_board_file set bo_table = '{board}', wr_id = '{wr_id}', \ bf_no = '{cnt}', bf_source = '{file}', bf_file = '{bf_file}', \ bf_content = '', bf_download = 0, bf_filesize = '{size}', \ bf_width = '{width}', bf_height = '{height}', bf_type = '{type}', bf_datetime = '{now}'" curs.execute(sql) # 파일의 개수를 게시물에 업데이트 sql = f"update g5_write_board set wr_file = '{file_count}' where wr_id = '{wr_id}'" curs.execute(sql) # MySQL connection 닫기 conn.close() return def main(): board = '게시판명을입력하세요' subject = '제목을입력하세요' content = '내용을입력하세요HTML태그도가능합니다' mb_id = '아이디를입력하세요' nickname = '닉네임을입력하세요' file_list = ['업로드할파일명을입력하세요', '업로드할파일명을입력하세요'] board_write(board, subject, content, mb_id, nickname, file_list) if __name__ == "__main__": main()
위 스크립트를 활용하여 3개의 파일(PNG, GIF, PY)을 업로드한 결과는 다음과 같습니다 ^^
영문이나 숫자가 아닌 한글로 된 파일명도 정상적으로 업로드되는 것을 확인했어요~
아미나에서 테스트하였지만 그누보드 5.4에서도 대동소이할 것으로 생각되네요 :)
(냥냥펀치!)
테스트해보니 작성자명을 계정의 실제 닉네임과 달리 적용한 경우에
위 함수로 글을 올린 후 로그인하여 글을 수정하면 해당 계정의 실제 닉네임으로 변경되는 점을 주의하셔야 됩니다!
crontab에 넣고 장기적으로 실사용하시려면 다음과 같은 부분을 보완하시는 것을 권장합니다 ^^
1. 오류 처리
파일 업로드 실패 등의 경우에 글과 첨부파일을 삭제하고 sys.exit() 하면 좋을 것 같네요.
2. 보안
사이트 운영자만 글 내용과 이미지를 선별하여 사용할 것이라면 별다른 보안 이슈는 없겠지만,
보안 관련하여 명랑폐인 님께서 올려주신 '게시판 파싱후 글등록 처리 함수'을 참고하시면 도움이 될듯요~
만약 타 사이트에서 파싱한 결과물을 위 함수를 이용하여 그대로 올리는 목적으로 사용하시려면
SQL injection, steganography 등 기법에도 대응할 수 있도록 보완해야 안전할 것 같습니다 ㅎㄷㄷ
허접한 스크립트인데 읽어주셔서 감사합니다!
다음에는 위 함수를 활용하여 브라우저에 접속하지 않은 상태에서
곧바로 글 작성 및 첨부파일 업로드를 할 수 있는 GUI 프로그램을 작성해볼게요~
그럼 다들 좋은 주말 되시고, 내일까지 비가 온다니 아침에 우산 꼭 챙기세요 ^-^
감사합니다!!