[TIL] 파일 출력 및 용량계산 23.09.06

이상훈·2023년 9월 6일
0

[내일배움캠프]

목록 보기
66/68

워크스페이스 디테일에서 현재 이 워크스페이스에 업로드 된 모든 파일을 가져오기 위해
쿼리빌더를 사용하여 모든 파일을 조회

// 워크스페이스에 업로드된 모든 파일 조회
  async getAllFiles(workspaceId: number): Promise<File[]> {
    const workspace = await this.workspaceRepository
      .createQueryBuilder('workspace')
      .innerJoinAndSelect('workspace.boards', 'boards')
      .innerJoinAndSelect('boards.board_columns', 'board_columns')
      .innerJoinAndSelect('board_columns.cards', 'cards')
      .where('workspace.id = :workspaceId', { workspaceId })
      .select([
        'cards.id',
        'cards.file_original_name',
        'cards.file_url',
        'cards.file_size',
        'cards.created_at',
        'cards.updated_at',
      ])
      .getRawMany();
    return workspace;
  }

필요한 컬럼들을 선택해 준 뒤 프론트에서 요청 시 데이터가 넘어가는 지 확인

원하는 정보가 넘어온 것을 확인하고 하나의 카드에 여러개의 파일이 존재하는 경우도 있으므로
다음과 같이 코드를 작성

// 전체파일 조회
async function getAllFiles() {
  try {
    await $.ajax({
      method: 'GET',
      url: `/workspaces/${workspaceId}/getFiles`,
      beforeSend: function (xhr) {
        xhr.setRequestHeader('Content-type', 'application/json');
        xhr.setRequestHeader('authorization', `Bearer ${accessToken}`);
      },
      success: async (data) => {
        let result = '';
        data.forEach((file) => {
          const fileOriginalName = file.cards_file_original_name;
          const fileUrl = file.cards_file_url;
          const fileSize = file.cards_file_size;
          let fileNames = [];
          let fileSizes = [];
          let fileUrls = [];

          if (typeof fileOriginalName === 'string') {
            try {
              fileNames = JSON.parse(fileOriginalName);
            } catch (err) {
              fileNames = [fileOriginalName];
            }
          } else {
            fileNames = fileOriginalName;
          }

          if (typeof fileSize === 'string') {
            try {
              fileSizes = JSON.parse(fileSize);
            } catch (err) {
              fileSizes = [parseInt(fileSize)];
            }
          } else {
            fileSizes = fileSize;
          }

          if (typeof fileUrl === 'string') {
            try {
              fileUrls = JSON.parse(fileUrl);
            } catch (err) {
              fileUrls = [fileUrl];
            }
          } else {
            fileUrls = fileUrl;
          }

          if (Array.isArray(fileNames) && fileNames.length > 0) {
            for (let i = 0; i < fileNames.length; i++) {
              const fileName = fileNames[i];
              const fileUrl = fileUrls[i];
              const imgSrc = getImgSource(fileName);
              const fileSize = getFileSize(fileSizes[i]);

              result += printFilesHtml(fileName, imgSrc, fileSize, fileUrl);
            }
          } else if (typeof fileOriginalName === 'string') {
            const fileName = fileOriginalName.replace(/"/g, '');
            const fileSize = getFileSize(fileSizes);
            const fileUrl = fileUrls;
            const imgSrc = getImgSource(fileName);

            result += printFilesHtml(fileName, imgSrc, fileSize, fileUrl);
          }
        });

        const totalSize = getTotalFileSize(data);
        const storage = await printStorageSize(totalSize);

        printStorage.innerHTML = storage;
        printFiles.innerHTML = result;
      },
    });
  } catch (err) {
    console.error(err);
  }
}

배열이 문자열로 넘어오는 형태기때문에 문자열이라면 JSON형태로 파싱해주는 코드를 작성하고
파일의 아래 용량을 표현해주기위해 파일사이즈를 구하는 함수를 작성

// 파일 메가바이트로 변환
function getFileSize(fileSize) {
  const fileSizeInMb = fileSize / (1024 * 1024);
  return fileSizeInMb.toFixed(2);
}

확장자에 따라 붙여주는 이미지가 다르므로 확장자 구분 함수도 작성

// 파일의 확장자 분류
function getImgSource(fileName) {
  const extension = fileName.split('.').pop().toLowerCase();
  const fileExtension = extension.replace(/"/g, '');
  let imgSrc = '';

  switch (fileExtension) {
    case 'jpg':
    case 'jpeg':
      imgSrc = './assets/img/jpg@2x.png';
      break;
    case 'png':
      imgSrc = './assets/img/png@2x.png';
      break;
    case 'zip':
      imgSrc = './assets/img/zip@2x.png';
      break;
    case 'pdf':
      imgSrc = './assets/img/pdf@2x.png';
      break;
    case 'psd':
      imgSrc = './assets/img/psd@2x.png';
      break;
    default:
      imgSrc = './assets/img/document.png';
      break;
  }
  return imgSrc;
}

출력결과

중간의 []빈 파일은 카드 수정 시 파일을 변경하지 않으면 기존파일이 그대로 들어있어야 하는데 빈값으로 변경되는 현상이 있어서 수정필요


파일출력의 구현이 끝났으면 파일 용량을 계산해줘야 하므로 getAllFiles()로 불러왔던 파일사이즈를 활용하여 전쳬용량을 계산

// 파일 전체용량 계산
function getTotalFileSize(files) {
  let totalSize = 0;
  for (const file of files) {
    const fileSize = file.cards_file_size;

    if (typeof fileSize === 'string') {
      const sizes = JSON.parse(fileSize);
      if (Array.isArray(sizes)) {
        for (const size of sizes) {
          totalSize += parseInt(size, 10) || 0;
        }
      } else {
        totalSize += parseInt(sizes, 10) || 0;
      }
    } else if (typeof fileSize === 'number') {
      totalSize += fileSize;
    }
  }

  const totalSizeInMb = totalSize / (1024 * 1024);

  return totalSizeInMb.toFixed(2);
}

무료버전의 제한용량이 100mb이므로 mb단위로 변환, parseInt는 10진수로 고정을 해주고 여기서 합산된 사이즈를 printStorageSize()로 보내줌

// 워크스페이스 용량 조회
async function printStorageSize(totalSize) {
  try {
    const results = await $.ajax({
      method: 'GET',
      url: `/workspaces/${workspaceId}`,
      beforeSend: function (xhr) {
        xhr.setRequestHeader('Content-type', 'application/json');
        xhr.setRequestHeader('authorization', `Bearer ${accessToken}`);
      },
    });

    const { data } = results;
    const totalSizeInGb = (totalSize / 1024).toFixed(2);
    const usagePercentageGb = ((totalSizeInGb / 10) * 100).toFixed(0);
    const usagePercentageMb = ((totalSize / 100) * 100).toFixed(0);

    if (data.memberships.length) {
      return `<div class="user-group-progress-bar">
                    <p>워크스페이스 사용량</p>
                    <div class="progress-wrap d-flex align-items-center mb-0">
                      <div class="progress">
                        <div
                          class="progress-bar bg-success"
                          role="progressbar"
                          style="width: ${usagePercentageGb}%"
                          aria-valuenow="${usagePercentageGb}"
                          aria-valuemin="0"
                          aria-valuemax="100"
                        ></div>
                      </div>
                      <span class="progress-percentage">${usagePercentageGb}%</span>
                    </div>
                    <span class="">10GB 중 ${totalSizeInGb}GB 사용</span>
                  </div>`;
    } else {
      return `<div class="user-group-progress-bar">
                    <p>워크스페이스 사용량</p>
                    <div class="progress-wrap d-flex align-items-center mb-0">
                      <div class="progress">
                        <div
                          class="progress-bar bg-success"
                          role="progressbar"
                          style="width: ${usagePercentageMb}&"
                          aria-valuenow="${usagePercentageMb}"
                          aria-valuemin="0"
                          aria-valuemax="100"
                        ></div>
                      </div>
                      <span class="progress-percentage">${usagePercentageMb}%</span>
                    </div>
                    <span class="">100MB 중 ${totalSize}MB 사용</span>
                  </div>`;
    }
  } catch (err) {
    console.error(err);
  }
}

멤버십을 가입한 워크스페이스라면 용량제한이 10gb이므로 gb로 사이즈를 변환해주고, 불러온 데이터 중 멤버십배열을 기준으로 있으면 10gb, 없으면 100mb로 html을 붙여줌

출력결과
무료버전인 경우

멤버십에 가입된 경우

profile
코린이

0개의 댓글