그저 내가 되었고

항해99) 1주차:: 웹개발+ 1주차; 파일 업로드 본문

개발/항해99 9기

항해99) 1주차:: 웹개발+ 1주차; 파일 업로드

hyuunii 2022. 9. 19. 20:51

🔮진행 순서

[1] 생김새 만들기

[2] 준비하기

[3] 서버 만들기

[4] 클라이언트 만들기


[1] 생김새 만들기

1. 사진 파일 입력하는 칸 만들어주기

1) 부트스트랩 컴포넌트(head에)

https://getbootstrap.com/docs/4.0/components/alerts/

 

2) HTML, CSS - 카드에 이미지 넣기(여기까지 하면 기존의 카드들에 이미지가 달라붙음)

  • 부트스트랩 컴포넌트 페이지의 card에서 이미지 태그 찾아 넣기 + static 폴더에 사진 넣고 가져오기
  • 코드👇🏻
<div class="card">
    <img src="../static/OIKAWA.jpg" class="card-img-top">
    <div class="card-body">
        <h5 class="card-title">오이카와상,,,</h5>
        <p class="card-text">암만 네가 잘나가도,,, 너는 여전히 내 아픈 손가락😭,,,,,</p>
    </div>
</div>

 

3) HTML,CSS - 폼 부분 파일 업로드 넣기(여기까지 하면 Browse 누르고 사진 선택했을 때 그 사진이 선택 창에 들어옴)

<사진 선택하는 칸 만들기>

<div class="custom-file">
  <input type="file" class="custom-file-input" id="customFile">
  <label class="custom-file-label" for="customFile">Choose file</label>
</div>

요👆🏻코드를  넣으면 사진 선택하는 칸이 생김.(아래 이미지에서 [사진 선택하기] 요 칸.이걸 적으면 바로 Browse 눌러서 사진 선택 '만' 가능, 아직 선택 창에는 안들어옴) id는 customFile에서 그냥 file(F 말고 f!!!!!!!!)

 

이걸 어디에 적어줘야하나면, 일단... body의 class 기준으로.

가장~~~~! 바깥에 wrap class가 jumbotron-fluid(저 백그라운드의 회색 직사각형 박스)와 card-colums(저장하기 눌러서 만든 각각의 카드들 전부 들어있는 클래스) 두 개를 갖고 있음.

그리고 jumbotron-fluid 안에 container class가 있고, 걔가 두 개의 하위 클래스(h1-나홀로 일기장-)과 posting-box 데리고 있음. 그리고 posting-box 안에 form-group(사진 제목 입력칸), form-group(내용 입력칸), button(저장하기)) 세 개를 품고 있음. 우리는 여기에! 사진 제목 입력칸 위에!!!!! 사진 업로드 창을 만들거니까, 그 위치에 코드를 집어 넣으면 됨.

 

아래👇🏻는 다 붙여 넣은 body의 wrap class 전체!!

<div class="wrap">
    <div class="jumbotron jumbotron-fluid">
        <div class="container">
            <h1>나홀로 일기장</h1>
            <div class="posting-box">
                <div class="custom-file">
                    <input type="file" class="custom-file-input" id="file">
                    <label class="custom-file-label" for="file">사진 선택하기</label>
                </div>
                <div class="form-group">
                    <input type="email" class="form-control" id="title" aria-describedby="emailHelp"
                           placeholder="사진 제목">
                </div>
                <div class="form-group">
                    <textarea class="form-control" id="content" rows="3"
                              placeholder="내용 입력"></textarea>
                </div>
                <button onclick="posting()" type="button" class="btn btn-primary">저장하기</button>
            </div>
        </div>
    </div>

    <div class="card-columns" id="cardsbox">
        <div class="card">
            <img class="card-img-top" src="../static/OIKAWA.png" alt="Card image cap">
            <div class="card-body">
                <h5 class="card-title">오이카와,,,</h5>
                <p class="card-text">암만 네가 잘나가도,,, 너는 여전히 내 아픈 손가락😭,,,,,</p>
            </div>
        </div>
    </div>

 

<선택한 사진이 선택하기 창에 들어오게 하기>

그리고, 여기서 파일업로드 라이브러리를 임포트👇🏻 해야만 선택한 파일이 사진 선택하기 창에 들어옴!!!(head에 작성)

<script src="https://cdn.jsdelivr.net/npm/bs-custom-file-input/dist/bs-custom-file-input.js"></script>

 

임포트 후, (document).ready에 한 줄 추가 필요

bsCustomFileInput.init()

위의 코드 적을거고, 결론적으로 이렇게👇🏻 됨.

<script>
        $(document).ready(function () {
            bsCustomFileInput.init()
            listing()
        })
        
          function listing() {
            $.ajax({
                type: "GET",
                
                ...

 

여기까지 하면, 이게👇🏻 가능.


[2] 준비하기

1. 서버 쪽에서 파일 받기 코드 먼저 작성

  save_to = 'static/mypicture.jpg'
    file.save(save_to)

👆🏻이거고(static이라는 경로 안에, mypicture.jpg라는 이름으로 저장 들어 간다는 뜻)

 

@app.route('/memo', methods=['POST'])

👆🏻이 부분에 붙을 것.

 

결과로 👇🏻이렇게 됨~!

@app.route('/diary', methods=['POST'])
def save_diary():
    title_receive = request.form["title_give"]
    content_receive = request.form["content_give"]

    file = request.files["file_give"]

    save_to = 'static/mypicture.jpg'
    file.save(save_to)

    doc = {
        'title': title_receive,
        'content': content_receive,
    }

    db.diary.insert_one(doc)

    return jsonify({'msg': '저장 완료!'})

 

 

2. 클라이언트 쪽에서 파일 보내기 코드 작성

function posting() {
    let title = $('#title').val()
    let content = $("#content").val()

    let file = $('#file')[0].files[0]
    let form_data = new FormData()

    form_data.append("file_give", file)
    form_data.append("title_give", title)
    form_data.append("content_give", content)

    $.ajax({
        type: "POST",
        url: "/diary",
        data: form_data,
        cache: false,
        contentType: false,
        processData: false,
        success: function (response) {
            alert(response["msg"])
            window.location.reload()
        }
    });
}

👆🏻이거고(기존의 맨 위의 두 줄 let title=~이랑 let content=~는 그대로 두고, 그 밑에... 새로 form_data.append로 파일 덕분에.. form_data라는 것에 실려져 보내짐(file은 그냥은 못 가고 꼭 form_data에 실려져 가야됨)

 

+) $('#file')[0].files[0] 왜 이렇게 복잡?! $('#file')[0]얘는 여기서 그냥 file 찍으면.. 우리가 body에서 id값 붙인 여기<input type="file" class="custom-file-input" id="file">가 많은 속성을 달고 있기 때문에 [0]을 딱 명시해줘야! 우리가 body에 적어준 그대로(<input type="file" class="custom-file-input" id="file">이렇게) 찍힘(콘솔창에서 확인 가능). 

뒤의 $('#file')[0].files[0] 이거는.. 없이 찍어보면 들어가 있는 files의 갯수가 나옴. 그렇기에 [0]을 찍어주면 방금 업로드한 파일이 선택됨

 

+) 파일 보낼 때 기본 속성이 최적화 되어 있지 않을 수 있기 때문에 👇🏻얘로 다 꺼주는 것

cache: false,
contentType: false,
processData: false,

 

function posting() {

여기 밑에 붙을 것.

 

결과로 👇🏻이렇게 됨~!(기존의.. 텍스트만 입력하는 post는 삭제ㄱㄱ~!)

function posting() {
            let title = $('#title').val()
            let content = $("#content").val()

            let file = $("#file")[0].files[0]
            let form_data = new FormData()

            form_data.append("file_give", file)
            form_data.append("title_give", title)
            form_data.append("content_give", content)

            $.ajax({
                type: "POST",
                url: "/diary",
                data: form_data,
                cache: false,
                contentType: false,
                processData: false,
                success: function (response) {
                    alert(response["msg"])
                    window.location.reload()
                }
            });
        }

[3] 서버 만들기

🔮새롭고 Fancy한 문법 두 개 연습

1. f-string

myname = '홍길동'
text = f'내 이름은 {myname}입니다.'

(text = '내 이름은' + {myname} +.... 이렇게 지저분하게 안써도 됨ㅎㅅㅎ)

 

2. datetime

from datetime import datetime

👆🏻임포트

 

now = datetime.now()
print(now)

👆🏻자주 쓰는 문법 1)지금 날짜 시간 찍기

 

today = datetime.now()
mytime = today.strftime("%Y-%m-%d-%H-%M-%S")or("%년-%월-%일-%시-%분-%초") //쓸 만큼 잘라 쓰기
print(mytime)

👆🏻자주 쓰는 문법 2)날짜 시간을 원하는 형태로 변환하기

 

 

 

위의 것 갑자기 왜,,,,,?

받을 image file 이름을 시-분-초까지 나오게 해서 겹칠 일 없도록 하자!(이름이 같은-예컨대 원래 했던 mypicture- 파일로 계속 저장? 그러면 계속 파일이 덧씌워지겠쥬,,, 안되자나염,,,,!!!)

 

그래서 결국 하고싶은건? 파일 이름을 이렇게👇🏻 지정해주려구~!

today = datetime.now()
mytime = today.strftime('%Y-%m-%d-%H-%M-%S')

filename = f'file-{mytime}'

(👆🏻이거 print(filename)으로 실행하면 file-2022-09-19-20-06-40 일케 뜬당!!!!)

 

 

그러면 차례대로!!

1) app.py(서버)에 임포트 먼저 해주고

from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db = client.dbsparta_plus_week1

from datetime import datetime


@app.route('/')
def home():
    return render_template('index.html')

 

2) 새로운 이름 만들어주고

@app.route('/diary', methods=['POST'])
def save_diary():
    title_receive = request.form["title_give"]
    content_receive = request.form["content_give"]

    file = request.files["file_give"]


    today = datetime.now()
    mytime = today.strftime('%Y-%m-%d-%H-%M-%S')

    filename = f'file-{mytime}'
    

    db.diary.insert_one(doc)

    return jsonify({'msg': '저장 완료!'})

 

3) 새로운 이름으로 저장해주고

@app.route('/diary', methods=['POST'])
def save_diary():
    title_receive = request.form["title_give"]
    content_receive = request.form["content_give"]

    file = request.files["file_give"]


    today = datetime.now()
    mytime = today.strftime('%Y-%m-%d-%H-%M-%S')

    filename = f'file-{mytime}'

    save_to = f'static/{filename}.jpg'
    file.save(save_to)

    doc = {
        'title': title_receive,
        'content': content_receive,
    }


    db.diary.insert_one(doc)

    return jsonify({'msg': '저장 완료!'})

 

4) jpg 말고 다른 이미지 파일도 있으니까,,,,, 확장자 추가해주자~!

extension = file.filename.split('.')[-1]

👆🏻요걸 app.py에 추가할건데(POST란), 확장자는 파일 네임의 가장 마지막 닷(.) 뒤의 글자들이니까!!! 저렇게 생겼당.

 

그리구 이제 같은 란의 좀 밑의 save_to에서 jpg 대신에 extension을 적음 된당. 이렇게!!! 👇🏻

    extension = file.filename.split('.')[-1]

    today = datetime.now()
    mytime = today.strftime('%Y-%m-%d-%H-%M-%S')

    filename = f'file-{mytime}'

    save_to = f'static/{filename}.{extension}'
    file.save(save_to)

 

5) mongodb가 우리가 저장하려는 사진이 각각 어떤 title과 content에 대응하는지 알아야 하니까👇🏻 마지막 한 줄 추가~!

    doc = {
        'title': title_receive,
        'content': content_receive,
        'file' : f'{filename}.{extension}'
    }

[4] 클라이언트 만들기

👉🏻우리가 저장한 사진들이 뜨도록~!(지금은,, 아직 고정된 img로 보여주고 있음)

 

1. 우선적으로, index.html의 listing()에 아래👇🏻 세 번째 줄의 코드 추가

let title = diaries[i]['title']
let content = diaries[i]['content']
let file = diaries[i]['file']

 

2. 아래의 let temp_html의 img class의 img src 바꿔주기!!!

let temp_html = `<div class="card">
	<img class="card-img-top" src="../static/'OIKAWA.png' alt="Card image cap">

👇🏻

let temp_html = `<div class="card">
	<img class="card-img-top" src="../static/${file}" alt="Card image cap">

 

3. 추가로 작성 날짜 같이 저장되게?!(약간 흐리게!!)

    date_receive =  today.strftime('%Y-%m-%d')

    doc = {
        'title': title_receive,
        'content': content_receive,
        'file' : f'{filename}.{extension}',
        'date' : date_receive
    }
let date = diaries[i]['date']

    let temp_html = `<div class="card">
           						 ...
                                    <p class="date" id="date">${date}</p>
                                 ...
    $('#cardsbox').append(temp_html)
  .date {
            opacity: 0.4;
        }

결과물:::

최애는 최애고.... 오이카와는 오이카와니까..........💙