python / nodejs file 다루기

3 minute read

Front-End에서 file 다루기

• input tag

<input id="file-box" type="file" />
const $fileBox = document.querySelector("#file-box");
console.log($fileBox.value) 선택한 file 경로
console.log($fileBox.files) 선택한 file 정보

• multipart/form-data

form 요소에 여러 입력이 있어 content-type이 서로 다를 경우, 이를 서버에서 처리하기 위해 enctype을 multipart/form-data로 정의해줍니다.

<form enctype="mulipart/form-data">
const formData = new Form()
fetch("http://localhost:3000", {
  method: "POST",
  body: formData
})

// new Form을 넣어서 보내면, 자동으로 mulipart/form-data로 전송됩니다.

• fileReader

file을 비동기로 읽은 web api입니다.

- onload
file이 load되었을때 실행되는 event listener입니다.

- readAsText
file을 text로 읽습니다.

- readAsArrayBuffer
file을 array buffer로 읽습니다.

- readAsDataURL
file을 array buffer로 읽습니다. (base64로 인코딩됩니다.)

- result
file을 읽은 결과를 반환하며, 데이터 형식은 읽기 작업(readAsText, readAsArrayBuffer 등)에 의해 정해집니다.

<input id="file-box" type="file" />
const $fileBox = document.querySelector("#file-box");

<script>
  $fileBox.addEventListener("change", (ev) => {
    const reader = new FileReader();
    reader.onload = (e) => console.log(e.target.result);

    reader.readAsText(ev.target.files[0])
  })
</script>

• blob

blob은 멀티미디어(이미지/사운드/비디오 등)을 다루기 위한, js 자료형입니다.

const blob = new Blob(array[, option])

🔎 file 또한 특정한 blob의 형태입니다.

- ArrayBuffer
ArrayBuffer는 byte로 이루어진 배열로, binary data를 저장하는 공간입니다. 만일 new ArrayBuffer(32)로 buffer를 만든다면, 32 byte의 저장공간을 만든것과 같습니다. 이 공간은 view interface(Int8Array, Int16Array 등)를 통해 읽고/쓸수 있습니다.

const buffer = new ArrayBuffer(16); //16byte 메모리 공간
const uint8s = new Uint8Array(buffer); // buffer를 unsigned 8bit의 저장소로 읽고/쓸수 있으며, unsigned 8bit이기에 0~255의 수를 8개의 공간에 할당할 수 있습니다.
(int16Array라면, -128 ~ 127 )

python file 다루기

• 파일 열기

- open
file 객체를 반환합니다.

f = open([file 경로], [모드])

🔎 모드는 w(쓰기)/r(읽기)/a(추가)가 있습니다.

• 파일 닫기

open으로 열려있는 file객체를 close로 닫아줍니다.

f.close()

nodejs file 다루기

• 파일 열기

- fs.open
fs.open은 file을 엽니다.

fs.open([file 경로], [w|r|a], (err, fd) => {
...
});

• multer

multer는 multipart/form-data를 다루기 위한 라이브러리입니다.
- 설치

npm install --save multer

- 예시 코드

<!-- client -->
<form action="/profile" method="post" enctype="multipart/form-data">
  <input type="file" name="avatar" />
</form>
// server
const express = require('exress');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

const app = express()

app.post('/upload', upload.single('avatar'), (req, res, next) => {
  ...

  //req.body에 담겨져있습니다.
})

- configuration

const upload = multer({
  dest: '/dist' // 저장될 directory 이름
  limit: { fileSize: 10 * 1024 * 1024 } // file 제한
  fileFilter: ... // 어떤 file을 다룰지 정의
  preserverPath: ...
})

- disk storage

const upload = multer({
  // server disk에 저장하며, destination/filename 정의합니다.
  storage: multer.diskStorage({
    // disk에 저장되는 directory를 설정합니다.
    destination(req, file, cb) {
      cb(null, [directory 이름])
    }
    // disk에 저장되는 file 이름을 설정합니다.
    filename: (req, file, cb) => {
      cb(null, [file 이름])
    }
  })
})

- middleware
multer는 middleware로 작동하여, single, array, field, none, any로 form에서 요청온 file들을 처리할 수 있습니다. multer는 이들 요청을 처리한 후 라우터 핸들러에 file 정보를 담아 보냅니다. (text type은 body에 담는듯?)

- single
단일 field에서 보내진 하나의 file을 처리하고, file 정보를 req.files에 담습니다.

app.post("/upload", upload.single("my-id"), (req, res, next) => {
  console.log(req.file);
});
// my-id field를 처리합니다.

- array
단일 field에서 보내진 여러개의 file들을 처리하고, file 정보를 req.files에 담습니다.

app.post("/upload", upload.array("my-id"), (req, res, next) => {
  console.log(req.files);
});
// my-id field를 처리합니다.

- field
여러 field에서 보내진 여러개의 file들을 처리하고, file 정보를 req.files에 담습니다.

app.post(
  "/upload",
  upload.single([{ name: "my-id" }, { name: "your-id" }]),
  (req, res, next) => {
    console.log(req.files);
  }
);
// my-id, your-id field를 처리합니다.

🔎 field 이름은 form에서 정의됩니다.

• socketio

- 데이터 보내기

// client
const handleChangeFileInputREST = async (ev) => {
  const file = ev.target.files[0];
  const formData = new FormData();
  formData.append("image", file);
  const res = await fetch("/portfolios/memos", {
    method: "POST",
    body: formData,
  });
  console.log(await res.json());
};
// client
const handleChangeFileInput = (ev) => {
  setFiles(ev.currentTarget.value);
  const reader = new FileReader();
  const file = ev.currentTarget.files[0];
  console.log(file.type);
  console.log(file.name);
  console.log(file.size);
  reader.readAsArrayBuffer(ev.currentTarget.files[0]);
  reader.onload = () => {
    console.log(reader.result);
    currentSocket.emit("file-upload", {
      type: "file-upload",
      payload: { binary: reader.result, mime: file.type, name: file.name },
      auth: { token },
    });
  };
};
// server
export async function fileUpload(data) {
  try {
    const namespace = this.nsp;
    const { binary, mime, name } = data.payload;

    const filePath = path.join(__dirname, "files");
    const lastDotIdx = name.lastIndexOf(".");
    const extension = name.substring(lastDotIdx);

    fs.openSync(`${filePath}/${name}`, "w");
    fs.writeFileSync(`${filePath}/${name}`, binary);

    const serviceKey = path.join(__dirname, "..", "config/gcp_bucket_key.json");

    const storage = new Storage({ keyFilename: serviceKey });
    const bucketname = "vos-bucket";
    const res = await storage.bucket(bucketname).upload(`${filePath}/${name}`, {
      destination: `memo_files/${name}`,
    });

    const url = res[0].metadata.mediaLink;
    // namespace.in(bookmarkId).emit('file-upload', {
    //     type: 'file-upload',
    //     status: 'success',
    //     message: 'file uploaded',
    //     payload:"..."
    // })
    // https://storage.googleapis.com/vos-bucket/memo_files/a
    // console.log(res)
  } catch (error) {
    this.emit("edit-memo", {
      type: "edit-memo",
      status: "error",
      message: error.message,
    });
  }
}

참고자료

• python 파일 읽기/쓰기
• mime type mdn
• javascript csv - a
• javascript csv - b
• javascript csv - c
• javascript csv - d
• form enctype 종류
• mutipart/form-data
• FileReader API
• FileReader API (readAsTest)
• FileReader API (onload)
• FileReader API (readAsArrayBuffer)
• FileReader API (readAsDataUrl)
• blob mdn • ArrayBuffer mdn
• ArrayBuffer

Updated: