programing

콘텐츠 디스포지션에서 파일 이름을 가져오는 방법

instargram 2023. 3. 28. 21:13
반응형

콘텐츠 디스포지션에서 파일 이름을 가져오는 방법

저는 Ajax의 응답으로 파일을 다운받았습니다.에서 파일 이름과 파일 형식을 가져오는 방법content-disposition썸네일을 표시합니다.검색 결과가 많이 나왔는데 길을 제대로 못 찾았어요.

$(".download_btn").click(function () {
  var uiid = $(this).data("id2");

  $.ajax({
    url: "http://localhost:8080/prj/" + data + "/" + uiid + "/getfile",
    type: "GET",
    error: function (jqXHR, textStatus, errorThrown) {
      console.log(textStatus, errorThrown);
    },
    success: function (response, status, xhr) {
      var header = xhr.getResponseHeader('Content-Disposition');
      console.log(header);     
    }
});

콘솔 출력:

inline; filename=demo3.png

옛날에는 이렇게 사용했어요.서버 응답으로 첨부 파일을 제공한다고 가정합니다.

REST 서비스에서 응답 헤더를 이렇게 설정했습니다.response.setHeader("Content-Disposition", "attachment;filename=XYZ.csv");

function(response, status, xhr){
    var filename = "";
    var disposition = xhr.getResponseHeader('Content-Disposition');
    if (disposition && disposition.indexOf('attachment') !== -1) {
        var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        var matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) { 
          filename = matches[1].replace(/['"]/g, '');
        }
    }
}

편집: 질문에 맞게 답변 편집 - 단어 사용inline대신attachment

function(response, status, xhr){
    var filename = "";
    var disposition = xhr.getResponseHeader('Content-Disposition');
    if (disposition && disposition.indexOf('inline') !== -1) {
        var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        var matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) { 
          filename = matches[1].replace(/['"]/g, '');
        }
    }
}

자세한 것은 이쪽

이것은 Marjon4의 답변을 개선한 것입니다.

선택한 답변에 대한 훨씬 간단한 방법은 다음과 같이 분할을 사용하는 것입니다.

var fileName = xhr.getResponseHeader('content-disposition').split('filename=')[1].split(';')[0];

주의: 파일 이름 자체에 세미콜론(;)이 포함되어 있는 경우 이 솔루션은 예상대로 작동하지 않을 수 있습니다.

파일명을 취득해, 이러한 이상한 URL 부호화 UTF-8 헤더와 ASCII 헤더를 서포트하는 경우는, 다음과 같은 것을 사용할 수 있습니다.

public getFileName(disposition: string): string {
    const utf8FilenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:; ?|$)/i;
    const asciiFilenameRegex = /^filename=(["']?)(.*?[^\\])\1(?:; ?|$)/i;

    let fileName: string = null;
    if (utf8FilenameRegex.test(disposition)) {
      fileName = decodeURIComponent(utf8FilenameRegex.exec(disposition)[1]);
    } else {
      // prevent ReDos attacks by anchoring the ascii regex to string start and
      //  slicing off everything before 'filename='
      const filenameStart = disposition.toLowerCase().indexOf('filename=');
      if (filenameStart >= 0) {
        const partialDisposition = disposition.slice(filenameStart);
        const matches = asciiFilenameRegex.exec(partialDisposition );
        if (matches != null && matches[2]) {
          fileName = matches[2];
        }
      }
    }
    return fileName;
}

주의사항:

  1. 이것은, UTF-8 파일명의 값을 가져갑니다(설정되어 있는 경우).
  2. 다운로드 시 브라우저는 특정 문자를 대체하기 위해 이름을 변경할 수 있습니다.",와 함께_(크롬)
  3. ascii 패턴은 따옴표로 묶인 파일 이름에 가장 적합하지만 따옴표로 묶지 않은 값을 지원합니다.이 경우 다음 텍스트가 모두 처리됩니다.filename=그리고 다음 날이나 다음 날 이전에;또는 헤더 값의 끝을 파일명으로 지정합니다.
  4. 이렇게 해도 경로 정보는 정리되지 않습니다.웹 사이트에서 파일을 저장하는 경우 브라우저의 작업이지만 노드 앱 등의 컨텍스트에서 파일을 사용하는 경우 OS별로 경로 정보를 정리하고 파일 이름만 남겨두십시오. 그렇지 않으면 시스템 파일을 덮어쓰는데 조작된 파일 이름이 사용될 수 있습니다(파일 이름).../../../../../../../path/to/system/files/malicious.dll)

MDN 콘텐츠 디스포지션 헤더

또는 단순히:

var fileName = xhr.getResponseHeader('Content-Disposition').split("filename=")[1];

이 경우 헤더는 다음과 같습니다.

attachment; filename="test-file3.txt"

따라서 이름 있는 그룹 regexp를 사용하여 파일 이름을 쉽게 추출할 수 있었습니다.

const regExpFilename = /filename="(?<filename>.*)"/;

const filename: string | null = regExpFilename.exec(contentDispositionHeader)?.groups?.filename ?? null;

OP에는 파일명 주위에 따옴표가 없기 때문에 조금 주제에서 벗어났다는 것을 알지만, 누군가 방금 전에와 같은 패턴을 발견했을 경우에 대비하여 공유합니다.

다음 솔루션을 사용해 보십시오.

var contentDisposition = xhr.getResponseHeader('Content-Disposition');
var startIndex = contentDisposition.indexOf("filename=") + 10; // Adjust '+ 10' if filename is not the right one.
var endIndex = contentDisposition.length - 1; //Check if '- 1' is necessary
var filename = contentDisposition.substring(startIndex, endIndex);
console.log("filename: " + filename)

content-disposition이라는 작업을 수행하는 npm 패키지가 있습니다.

이게 도움이 될 거라 믿어요!

let filename = response.headers['content-disposition'].split('filename=')[1].split('.')[0];
let extension = response.headers['content-disposition'].split('.')[1].split(';')[0];

에서는, 「이러다」의 되고 있습니다.filename 문자 Unicode 문자)가되어 있습니다.-, !, (, ))ㄹ 수 있다( ) utf-8부호화)"부호화" 형식으로 합니다.filename*=utf-8''Na%C3%AFve%20file.txt(자세한 내용은 여기를 참조해 주세요).이 경우 이 함수는 다음을 디코딩하는 데 사용됩니다.filename.

const disposition = xhr.getResponseHeader('Content-Disposition');
filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1]
if (filename.toLowerCase().startsWith("utf-8''"))
    filename = decodeURIComponent(filename.replace("utf-8''", ''))
else
    filename = filename.replace(/['"]/g, '')

크로스 오리진 요청을 수행하는 경우 다음 항목을 반드시 추가하십시오.Access-Control-Expose-Headers: Content-Disposition서버측의 응답 헤더(를 참조), 헤더를 공개하기 위해서, 그 이외의 경우는,filename자바스크립트§:

headers = {'Access-Control-Expose-Headers': 'Content-Disposition'}
return FileResponse("Naïve file.txt", filename="Naïve file.txt", headers=headers)

브라우저에서도 사용할 수 있는 라이브러리 콘텐츠 배치 첨부 파일이 있습니다.

npm i -D content-disposition-attachment
import { AxiosResponse } from "axios";
import { parse } from "content-disposition-attachment";

const getFilenameFromHeaders = ({ headers }: AxiosResponse<Blob>) => {
  const defaultName = "untitled";
  try {
    const { attachment, filename } = parse(headers["content-disposition"]);
    return attachment ? filename : defaultName;
  } catch (e) {
    console.error(e);
    return defaultName;
  }
};

멀티파트 바디를 사용하지 않는 경우 이 기능을 사용할 수 있습니다.Content-Disposition 헤더 값(string: inline; filename=filename3.png 등)에서 파일 이름을 추출하여 필요에 따라 디코딩합니다.

const getFileNameFromContentDisposition = disposition => { 
    if (disposition
        && (disposition.startsWith('attachment') || disposition.startsWith('inline'))
    ) {
        let filename = disposition.startsWith('attachment')
            ? disposition.replace("attachment;", "")
            : disposition.replace("inline;", ""); //replaces first match only
        filename = filename.trim();
        if (filename.includes("filename*=") && filename.includes("filename=")) {
            let filenames = filename.split(";"); //we can parse by ";" because all ";"s inside filename are escaped
            if (filenames.length > 1) { //"filename=" or "filename*=" not inside filename
                if (filenames[0].trim().startsWith("filename*=")) { //"filename*=" is preferred
                    filename = filenames[0].trim();
                } else {
                    filename = filenames[1].trim();
                }
            }
        }
        if (filename.startsWith("filename*=")) {
            filename = filename.replace("filename*=", "")
            .split("''").slice(1).join("''"); //remove encoding and ''
            filename = decodeURIComponent(filename);
        } else if (filename.startsWith("filename=")) {
            filename = filename.replace("filename=", "")
            if (filename.startsWith('"') && filename.endsWith('"')) {
                filename = filename.slice(1, filename.length - 1); //remove quotes
            }
        }
        return filename;
    }
}

함수의 결과는 다음과 같이 이름과 확장자로 나눌 수 있습니다.

let name = getFileNameFromContentDisposition("inline; filename=demo.3.png").split(".");
let extension = name[name.length - 1];
name = name.slice(0, name.length - 1).join(".");
console.log(name); // demo.3
console.log(extension); //png

예를 들어 svg를 사용하여 섬네일을 표시할 수 있습니다.

let colors = {"png": "red", "jpg": "orange"};
//this is a simple example, you can make something more beautiful
let createSVGThumbnail = extension => `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" viewBox="0 0 18 20">
    <rect x="0" y="0" width="18" height="20" fill = "#FAFEFF"/>
    <rect x="0" y="7" width="18" height="6" stroke="${colors[extension] || "blue"}" fill = "${colors[extension] || "blue"}"/>
    <text stroke = "white" fill = "white" font-size = "6" x = "0" y = "12.5" textLength = "18">${extension.toUpperCase()}</text>
</svg>`;

...

//You can use it as HTML element background-image
let background = "data:image/svg+xml;base64," + btoa(new TextDecoder().decode(createSVGThumbnail("png"))); 

언급URL : https://stackoverflow.com/questions/40939380/how-to-get-file-name-from-content-disposition

반응형