이미지 업로드
profile.html
<div class="my_info">
<img id="user_profile_img" src="/static/profile_images/{{ users.profile_img }}" alt="">
<div class="info_box">
<div class="nick_name">
{{ users.nickname }}
<button class="profile_btn" type="button" onclick="change_profile()">프로필 사진 변경</button>
</div>
...
</div>
...
</div>
프로필 사진을 출력하는 공간 <img id="user_profule_img" ...>
프로필 사진 변경 버튼 클릭 시 change_profile() 함수 호출하도록 설정
function change_profile() {
var url = '/change_profile';
var name = '프로필 사진 변경';
var option = 'width = 800, height = 800, top = 100, left = 200, location = no'
window.open(url, name, option);
}
팝업창을 띄우기 위해서 window.open(url, name, option) 함수 사용 (변수로 선언해서 내용 지정한 뒤 파라미터로 전달)
app.py
@app.route('/change_profile')
def change_profile():
# 현재 컴퓨터에 저장 된 'mytoken'인 쿠키 확인
token_receive = request.cookies.get('mytoken')
# 암호화되어있는 token의 값 디코딩(암호화 풀기)
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
my_info = db.USER.find_one({'id': payload['id']})
return render_template('/profile/change_profile.html', my_info=my_info)
팝업창에 change_profile.html 화면을 띄우고 로그인되어 있는 유저의 정보도 전달해줘야하기 때문에 해당 정보를 토큰에서 가져와서 그 유저의 정보를 my_info에 담아서 리턴
change_profile.html
<div class="file-upload">
<input type="hidden" id="nickname" value="{{ my_info.nickname }}">
<input type="file" id="file">
<button onclick="posting('{{ my_info.nickname }}')">업로드</button>
</div>
<hr>
<div>
<img src="/static/profile_images/{{ my_info.profile_img }}" id="preview">
</div>
body 부분 html 리턴 받은 my_info에서 nickname을 가져오고 기존 이미지를 미리보기 해주기 위해서 jinja 이용해서 사진 출력 해주는 코드 그리고 업로드 버튼 눌렀을 때 posting() 함수가 호출되고 마찬가지로 유저의 닉네임 값을 전달 해주기 위해서 파라미터로 줬다.
$(document).ready(function () {
$('#file').change(function () {
setImageFromFile(this, '#preview');
});
function setImageFromFile(input, expression) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$(expression).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
})
변경할 사진을 선택할 경우 업로드한 사진을 미리보기 할 수 있도록 하는 코드
this를 이용해서 업로드된 파일을 감지해서 정확히는 변경점과 이미지 태그의 id 값(#preview)을
setImageFromFile() 함수의 인자를 주고 해당 함수에서 첫번째 input 받은 값이 파일인지 아닌지 판별한 뒤
파일이 맞다면 해당 파일을 읽기 위해서 FileReader() 클래스의 인스턴스 생성하여 onload() 함수를 이용해서
파일이 읽혀지면 id="preview" 의 태그의 src="" 내용을 onload한 대상의 결과 즉, 읽은 파일의 내용으로 바꾸도록 attr 함수를 사용했다. 마지막에 reder.readAsDataURL(input.files[0]) 경우는 이미지 파일을 base64 형태로 인코딩해서 이미지를 다루기 좋도록 만들어 주는 코드입니다. 이 코드를 사용하지 않으면 흔히 아는 경로값이 String으로 나오는게 아니라 인코딩 되기전에 전혀 알 수 없는 이미지 값의 코드가 나온다고 생각하면된다.
function posting(nickname) {
alert('사진 변경 시작!')
let nick = nickname
let file = $('#file')[0].files[0]
let form_data = new FormData()
form_data.append("nick_give", nick)
form_data.append("file_give", file)
$.ajax({
type: "POST",
url: "/fileupload",
data: form_data,
cache: false,
contentType: false,
processData: false,
success: function (response) {
alert(response["result"])
opener.parent.location.reload();
window.close();
}
});
}
사진 전송을 위해서는 파일을 담아야하기 때문에 일반 변수들 처럼 val()해서 가져오는 것이 아닌 .files[0]을 해서 가져와야한다. 데이터를 담기 위해서도 FormData() 클래스의 인스턴스를 생성해서 해당 인스턴스에 담아서 request 해야한다.
form_data에 다양한 데이터들을 담았기 때문에 반드시 cache , contentType, processData 설정을 false해줘야 문제 없이 작동한다. 성공 시 즉, 이미지파일이 업로드 되고 나서 팝업 창을 닫고 부모창을 리로드하도록 코드를 작성했다.
app.py
# 방식1 : DB에는 파일 이름만 넣어놓고, 이미지 자체는 서버 컴퓨터에 저장하는 방식
@app.route('/fileupload', methods=['POST'])
def file_upload():
print('file_upload() 도착')
nick_receive = request.form['nick_give']
file = request.files['file_give']
# 해당 파일에서 확장자명만 추출
extension = file.filename.split('.')[-1]
# 파일 이름이 중복되면 안되므로, 지금 시간을 해당 파일 이름으로 만들어서 중복이 되지 않게 함!
today = datetime.datetime.utcnow()
mytime = today.strftime('%Y-%m-%d-%H-%M-%S')
filename = f'{nick_receive}-{mytime}'
# 파일 저장 경로 설정 (파일은 db가 아니라, 서버 컴퓨터 자체에 저장됨)
save_to = f'static/profile_images/{filename}.{extension}'
# 파일 저장!
file.save(save_to)
db.USER.update_one({'nickname': nick_receive}, {'$set': {'profile_img': f'{filename}.{extension}'}})
feed = list(db.FEED.find({'nickname': nick_receive}))
db.FEED.update_many({'nickname': nick_receive}, {'$set': {'profile_img': f'{filename}.{extension}'}})
for nick in feed:
print(nick)
return jsonify({'result': 'success'})
파일을 받을 때는 request.file['지정한 이름'] 으로 받아와야한다. 파일을 저장하기 위해서 확장자명 따로 추출하고 파일 이름의 중복을 막기 위해서 현재시간을 파일 이름으로 만들어서 중복되지 않도록 datetime 클래스의 함수 사용
srftime()으로 시간 형식을 지정하고 파일이름은 닉네임 - 시간 으로 지정해서 최종으로 실제 저장할 경로 값까지 추가해서 file.save(save_to) 함수로 컴퓨터에 저장하고
DB에 이미지 이름을 저장하는 것으로 완료 이를 출력하기 위해서 src="/static/경로/{{ 리턴 받은 값 }}" 형태로 출력
<img id="user_profile_img" src="/static/profile_images/{{ users.profile_img }}" alt="">
이미지 드래그 앤 드랍으로 업로드 후 게시글 작성
<div class="mdbox-post">
<div class="post-title">새 게시물 만들기</div>
<img alt=img-upload class="post-img" src="{{url_for('static', filename = 'nav-img/posting.png')}}">
<div class="post-info">사진과 동영상을 여기에 끌어다 놓으세요</div>
</div>
<div>
<textarea id="input_content" cols="12" rows="12"></textarea>
</div>
<div>
<button type="button" id="write_feed_btn">공유하기</button>
</div>
$(document).ready(function () {
// 이미지 드래그 앤 드랍으로 업로드
$('.mdbox-post')
.on("dragover", dragOver)
.on("dragleave", dragOver)
.on("drop", uploadFiles);
function dragOver(e) {
e.stopPropagation();
e.preventDefault();
if (e.type == "dragover") {
console.log('dragover')
$(e.target).css({
"background-color": "black",
"outline-offset": "-20px"
});
} else {
console.log('dragleave')
$(e.target).css({
"background-color": "white",
"outline-offset": "-10px"
});
}
}
{# 파일을 담을 변수 선언 이후 게시판 작성하기 할때 해당 함수에 담겨져있는 파일 가져가기 위함#}
let files;
function uploadFiles(e) {
{# drop했을 때 해당 함수 실행되며 파라미터로 drop한 데이터 가져옴 #}
e.stopPropagation();
e.preventDefault();
e.dataTransfer = e.originalEvent.dataTransfer;
files = e.dataTransfer.files; {# drop받은 데이터 파일형태로 files에 저장 #}
if (files.length > 1) {
alert('하나만');
return;
}
console.log('하나 판별 완료')
if (files[0].type.match(/image.*/)) {
$('#input_image').css({
"background-image": "url(" + window.URL.createObjectURL(files[0]) + ")",
"outline": "none",
"background-size": "contain",
"background-repeat": "no-repeat",
"background-position": "center"
});
} else {
alert('이미지가 아닙니다.');
return;
}
}
// 게시글 공유하기 버튼 클릭했을 때
$('#write_feed_btn').on('click', () => {
const content = $('#input_content').val();
const profile_image = $('#input_profile_image').attr('src');
const user_nick = $('#user_nickname').text();
const file = files[0];
let fd = new FormData();
fd.append('file', file);
fd.append('content', content);
fd.append('profile_image', profile_image);
fd.append('user_nick', user_nick);
if (image.length <= 0) {
alert("이미지가 비어있습니다.");
} else if (content.length <= 0) {
alert("설명을 입력하세요");
} else if (profile_image.length <= 0) {
alert("프로필 이미지가 비어있습니다.");
} else if (user_nick.length <= 0) {
alert("사용자 id가 없습니다.");
} else {
alert('writeFeed()로 왓다')
$.ajax({
url: '/api/feed/upload',
data: fd,
method: "POST",
processData: false,
contentType: false,
success: function (data) {
alert('게시물 작성 성공!')
},
error: function (request, status, error) {
alert('게시물 작성 실패!')
}
})
}
});
});
@app.route('/api/feed/upload', methods=['POST'])
def feed_upload():
token_receive = request.cookies.get('mytoken')
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
my_follow = db.USER.find_one({'id': payload['id']})
file = request.files['file']
content = request.form['content']
user_nick = request.form['user_nick']
# 해당 파일에서 확장자명만 추출
extension = file.filename.split('.')[-1]
# 파일 이름이 중복되면 안되므로, 지금 시간을 해당 파일 이름으로 만들어서 중복이 되지 않게 함!
today = datetime.datetime.utcnow()
mytime = today.strftime('%Y-%m-%d-%H-%M-%S')
filename = f'{user_nick}-{mytime}'
# 파일 저장 경로 설정 (파일은 db가 아니라, 서버 컴퓨터 자체에 저장됨)
save_to = f'static/upload/{filename}.{extension}'
# 파일 저장!
file.save(save_to)
count_list = [0]
feed_list = list(db.FEED.find({}, {'num': True, '_id': False}))
for number in feed_list:
print(number['num'])
count_list.append(int(number['num']))
count = max(count_list) + 1
# 아래와 같이 입력하면 db에 추가 가능!
doc = {
'num': str(count),
'date': mytime,
'nickname': user_nick,
'feed_images': [f'{filename}.{extension}'],
'profile_img': my_follow['profile_img'],
'content': content,
'like': [],
'bookmark': [],
'reply': []
}
db.FEED.insert_one(doc)
return jsonify({'result': 'success'})
ajax 비동기식 유효성검사
join.html
<div className="box-input"> <!-- input box -->
<input className="input-join" type="text" id="id" placeholder="휴대폰 번호 또는 이메일 주소">
<span id="id_check"></span><!-- input id -->
<input className="input-join" type="text" id="name" placeholder="성명">
<span id="name_check"></span><!-- input name -->
<input className="input-join" type="text" id="nickname" placeholder="사용자 이름">
<span id="nick_check"></span><!-- input nickname -->
<input className="input-join" type="password" id="pwd" placeholder="비밀번호">
<span id="pwd_check"></span><!-- input pw -->
</div>
<!--입력 값이 전부 올바르게 입력되었는지 판별을 위한 boolean값을 저장하기 위한 type="hidden"태그 들-->
<input type="hidden" id="hidden-id" value="false">
<input type="hidden" id="hidden-name" value="false">
<input type="hidden" id="hidden-nick" value="false">
<input type="hidden" id="hidden-pwd" value="false">
<div class="box-button"> <!-- button box -->
<button class="button" type="button" onclick="join()">가입</button> <!-- button -->
</div>
// id 값을 입력했을 때
$('#id').blur(function () {
let id = $('#id').val()
// 입력받은 값 중 입력하지 않은 값이 있을 경우
if (id == "") {
$('#id_check').html('<i class="fa-solid fa-circle-exclamation"></i>')
$('#hidden-id').val('false')
} else {
// 아이디 중복 확인
$.ajax({
type: "POST",
url: '/api/id_dup',
data: {'id_give': id},
success: function (response) {
if (response['duplicate']) {
$('#id_check').html('<i class="fa-regular fa-circle-xmark"></i>')
$('#hidden-id').val('false')
} else {
// 정규 표현식을 이용한 아이디 형식 제한 영문 소문자, 대문자, 숫자, 4-30자
let id_regExp = /^[a-zA-Z0-9]([a-zA-Z0-9]*)(@)([a-zA-Z0-9]*)(\.)([a-zA-Z]*){4,40}$/;
if (!id_regExp.test(id)) {
$('#id_check').html('<i class="fa-regular fa-circle-xmark"></i>')
$('#hidden-id').val('false')
} else {
$('#id_check').html('<i class=\"fa-regular fa-circle-check\"></i>')
$('#hidden-id').val('true')
}
}
}
})
}
})
function join() {
if ($('#hidden-id').val() == 'true') {
if ($('#hidden-nick').val() == 'true') {
if ($('#hidden-name').val() == 'true') {
if ($('#hidden-pwd').val() == 'true') {
// 입력한 값들 가져와 변수에 저장
let id = $('#id').val()
let name = $('#name').val()
let nick = $('#nickname').val()
let pwd = $('#pwd').val()
$.ajax({
type: "POST",
url: "/api/join",
data: {'id_give': id, 'name_give': name, 'nick_give': nick, 'pwd_give': pwd},
success: function (response) {
alert(response["msg"])
window.location.href = '/login'
}
})
} else {
alert('패스워드는 영대소문자, 숫자 특수문자 8-20자 입니다.!')
}
} else {
alert('이름은 한글, 영문 2-20자입니다.')
}
} else {
alert('닉네임이 중복이거나 영대소문자,한글,숫자, 4-12자에 맞지않습니다.')
}
} else {
alert('아이디가 중복 또는 이메일 형식이 아닙니다.')
}
}
app.py
# ajax에서 비동기식으로 아이디 중복 확인을 위해 따로 함수 정의
@app.route("/api/id_dup", methods=["POST"])
def id_dup():
id_receive = request.form['id_give']
id_dup = bool(db.USER.find_one({'id': id_receive}))
return jsonify({'duplicate': id_dup})
# ajax에서 비동기식으로 닉네임 중복 확인을 위해 따로 함수 정의
@app.route("/api/nick_dup", methods=["POST"])
def nick_dup():
nick_receive = request.form['nick_give']
nick_dup = bool(db.USER.find_one({'nickname': nick_receive}))
return jsonify({'duplicate': nick_dup})
# 회원가입 입력받은 값을 받아 DB에 추가하기
@app.route("/api/join", methods=["POST"])
def api_join():
# data = json.loads(request.data) request.data로 get()으로 받아오는 방식으로 사용가능
# id_receive = data.get('id_give')
id_receive = request.form['id_give']
name_receive = request.form['name_give']
nick_receive = request.form['nick_give']
pwd_receive = request.form['pwd_give']
# 입력받은 패스워드 값 해싱하여 암호화
hashed_pw = hashlib.sha256(pwd_receive.encode('utf-8')).hexdigest()
doc = {
'id': id_receive,
'pwd': hashed_pw,
'name': name_receive,
'nickname': nick_receive,
'follower': [],
'following': [],
'like': [],
'bookmark': [],
'profile_img': 'profile_default.png'
}
db.USER.insert_one(doc)
return jsonify({'result': 'success', 'msg': '회원 가입 완료'})
'레퍼런스' 카테고리의 다른 글
og 태그(미리보기) (0) | 2022.04.23 |
---|---|
AWS 서버 구매, 설정, 실행 + 도메인 연결(가비아) (0) | 2022.04.23 |
flask서버연동, DB레퍼런스, 크롤링(BeautifulSoup) (0) | 2022.04.22 |
파이썬 패키지 설치 (0) | 2022.04.22 |
HTML& CSS 기본태그 & 부트스트랩 시작코드 및 주소 (0) | 2022.04.22 |