파일을 업로드하기위한 fileReader.readAsBinaryString


83

fileReader.readAsBinaryString을 사용하여 AJAX를 통해 서버에 PNG 파일을 업로드하려고 시도하고 코드를 제거했습니다 (fileObject는 내 파일에 대한 정보를 포함하는 객체입니다).

var fileReader = new FileReader();

fileReader.onload = function(e) {
    var xmlHttpRequest = new XMLHttpRequest();
    //Some AJAX-y stuff - callbacks, handlers etc.
    xmlHttpRequest.open("POST", '/pushfile', true);
    var dashes = '--';
    var boundary = 'aperturephotoupload';
    var crlf = "\r\n";

    //Post with the correct MIME type (If the OS can identify one)
    if ( fileObject.type == '' ){
        filetype = 'application/octet-stream';
    } else {
        filetype = fileObject.type;
    }

    //Build a HTTP request to post the file
    var data = dashes + boundary + crlf + "Content-Disposition: form-data;" + "name=\"file\";" + "filename=\"" + unescape(encodeURIComponent(fileObject.name)) + "\"" + crlf + "Content-Type: " + filetype + crlf + crlf + e.target.result + crlf + dashes + boundary + dashes;

    xmlHttpRequest.setRequestHeader("Content-Type", "multipart/form-data;boundary=" + boundary);

    //Send the binary data
    xmlHttpRequest.send(data);
}

fileReader.readAsBinaryString(fileObject);

업로드하기 전에 (VI 사용) 파일의 처음 몇 줄을 살펴보면

여기에 이미지 설명 입력

업로드 후 동일한 파일이 표시됩니다.

여기에 이미지 설명 입력

그래서 어딘가에 서식 / 인코딩 문제처럼 보였고, 원시 바이너리 데이터에서 간단한 UTF8 인코딩 기능을 사용해 보았습니다.

    function utf8encode(string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    )

그런 다음 원래 코드에서

//Build a HTTP request to post the file
var data = dashes + boundary + crlf + "Content-Disposition: form-data;" + "name=\"file\";" + "filename=\"" + unescape(encodeURIComponent(file.file.name)) + "\"" + crlf + "Content-Type: " + filetype + crlf + crlf + utf8encode(e.target.result) + crlf + dashes + boundary + dashes;

나에게 출력을 제공합니다

여기에 이미지 설명 입력

여전히 원시 파일이 무엇인지 = (

인코딩 문제를 피하기 위해 파일을 인코딩 /로드 / 처리하는 방법은 HTTP 요청에서 수신되는 파일이 업로드되기 전의 파일과 동일합니다.

다른 유용한 정보, fileReader.readAsBinaryString ()을 사용하는 대신 fileObject.getAsBinary ()를 사용하여 바이너리 데이터를 가져 오면 제대로 작동합니다. 그러나 getAsBinary는 Firefox에서만 작동합니다. 나는 Mac에서 Firefox와 Chrome에서 이것을 테스트했으며 둘 다 동일한 결과를 얻었습니다. 백엔드 업로드는 Mac에서 다시 실행 되는 NGINX 업로드 모듈에 의해 처리됩니다 . 서버와 클라이언트가 동일한 시스템에 있습니다. 내가 업로드하려는 모든 파일에서 똑같은 일이 발생합니다. 가장 분명한 예이기 때문에 PNG를 선택했습니다.

답변:


74

을 사용 fileReader.readAsDataURL( fileObject )하면 base64로 인코딩되어 서버에 안전하게 업로드 할 수 있습니다.


8
작동하는 동안 서버에 저장된 파일의 버전은 Base64로 인코딩됩니다. Base64 인코딩이 아닌 바이너리 데이터로 전송할 수있는 방법이 없습니까 (IE가 일반 <input type="file">필드를 사용하여 업로드 된 것처럼 )
Smudge

2
서버에 PHP가있는 경우 저장하기 전에 base64_decode (file) 할 수 있습니다. 그리고 아니요-http를 통해 원시 바이너리 데이터를 전송하는 안전한 방법이 없습니다.
c69

readAsDataURL를 사용하는 것은 나에게이 제공 imgur.com/1LHya을 (우리는 실제로 파이썬을 사용하고 있지만, PHP는 좋은 시험입니다) 내가 할 PHP의 base64_decode를 통해 다시 실행하는 서버에 imgur.com/0uwhy를 , 아직도 원래의 이진 데이터 및 유효한 화상 = (
얼룩

20
@ imgur.com/1LHya O, 내 나쁜! 서버에서 base64 문자열을 ""로 분할하고 두 번째 부분 만 저장해야합니다. 따라서 MIME 유형은 실제 파일 내용과 함께 저장되지 않습니다.
c69

7
아니 효율적이지 않습니다. 이렇게하면 파일 크기가 137 % 증가하고 서버 오버 헤드가 발생합니다. 하지만 F *** IE를 지원하는 다른 방법은 없습니다
puchu

109

(다음은 늦었지만 완전한 답변입니다)

FileReader 메서드 지원


FileReader.readAsBinaryString()한다 추천하지 않습니다. 그것을 사용하지 마십시오! 더 이상 W3C 파일 API 작업 초안에 없습니다 .

void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, optional DOMString encoding);
void readAsDataURL(Blob blob);

NB : File이것은 일종의 확장 된 Blob구조입니다.

Mozilla는 여전히 MDN FileApi 문서readAsBinaryString() 에서이를 구현 하고 설명합니다 .

void abort();
void readAsArrayBuffer(in Blob blob); Requires Gecko 7.0
void readAsBinaryString(in Blob blob);
void readAsDataURL(in Blob file);
void readAsText(in Blob blob, [optional] in DOMString encoding);

readAsBinaryString()지원 중단 의 이유 는 다음과 같습니다. JavaScript 문자열의 표준은 DOMString임의의 바이너리 데이터가 아닌 UTF-8 문자 만 허용한다는 것입니다. 따라서 readAsBinaryString ()을 사용하지 마십시오. 그것은 안전하지 않으며 ECMAScript를 전혀 준수하지 않습니다.

우리는 JavaScript 문자열이 바이너리 데이터를 저장해서는 안되지만 Mozilla는 어떤 종류의 데이터를 저장할 수 있다는 것을 알고 있습니다. 제 생각에는 위험합니다. Blob그리고 typed arrays( ArrayBuffer아직 구현되지 않았지만 필요하지 않은 StringView)는 한 가지 목적으로 발명되었습니다 : UTF-8 문자열 제한없이 순수한 바이너리 데이터를 사용할 수 있도록합니다.

XMLHttpRequest 업로드 지원


XMLHttpRequest.send() 다음과 같은 호출 옵션이 있습니다.

void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);

XMLHttpRequest.sendAsBinary() 다음과 같은 호출 옵션이 있습니다.

void sendAsBinary(   in DOMString body );

sendAsBinary ()는 표준이 아니며 Chrome에서 지원되지 않을 수 있습니다.

솔루션


따라서 몇 가지 옵션이 있습니다.

  1. send()FileReader.resultFileReader.readAsArrayBuffer ( fileObject ). 조작하는 것이 더 복잡하지만 (별도의 send ()를 만들어야 함) RECOMMENDED APPROACH 입니다.
  2. send()FileReader.resultFileReader.readAsDataURL( fileObject ). 쓸모없는 오버 헤드와 압축 대기 시간을 생성하고 서버 측에서 압축 해제 단계가 필요하지만 Javascript에서 문자열로 조작하기 쉽습니다.
  3. 가되는 비표준 및 의sendAsBinary()FileReader.resultFileReader.readAsBinaryString( fileObject )

MDN은 다음과 같이 말합니다.

바이너리 콘텐츠를 전송하는 가장 좋은 방법 (파일 업로드 등)은 send () 메서드와 함께 ArrayBuffers 또는 Blob을 사용하는 것입니다. 그러나 문자열 화 가능한 원시 데이터를 보내려면 대신 sendAsBinary () 메서드를 사용하거나 StringView (기본이 아닌) 유형의 배열 수퍼 클래스를 사용하십시오.


10
나는 다시 발굴 단지 통해 바이너리 데이터 (등 PDF 파일)를 보낼 것을 아마 가장 쉬운 방법을 추가하고 싶었 죄송 FileReader.readAsDataURL및에 onload핸들러 대신에 단지를 전송 event.target.result(깨끗한 base64로 인코딩 된 문자열하지 않은)를 먼저 정규식으로 정리 event.target.result = event.target.result.match(/,(.*)$/)[1]하고 실제 base64를 서버로 보내 디코딩하십시오.

누구나 MDN을 편집 할 수 있으므로 소스로 사용하지 않을 것입니다.
Chris Anderson

4
@ user1299518, 더 나은 사용 event.target.result.split(",", 2)[1], 아닙니다 match.
MrKsn

1
@KrisWebDev : 권장 옵션에서 별도의 send ()를 만들어야한다고 언급했습니다. 왜?
Readren

권장 방법은 TFS REST API를 사용하여 첨부 파일을 업로드하는 데 효과적이었습니다. 고마워!
RoJaIt

24

이를 지원하는 브라우저에서 가장 좋은 방법은 파일을 Blob으로 보내거나 멀티 파트 양식을 원하는 경우 FormData를 사용하는 것입니다. 이를 위해 FileReader가 필요하지 않습니다. 이것은 데이터를 읽는 것보다 더 간단하고 효율적입니다.

특별히로 보내려면 multipart/form-dataFormData 객체를 사용할 수 있습니다.

var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("POST", '/pushfile', true);
var formData = new FormData();
// This should automatically set the file name and type.
formData.append("file", file);
// Sending FormData automatically sets the Content-Type header to multipart/form-data
xmlHttpRequest.send(formData);

을 사용하는 대신 데이터를 직접 보낼 수도 있습니다 multipart/form-data. 설명서를 참조하십시오 . 물론 이것은 서버 측 변경도 필요합니다.

// file is an instance of File, e.g. from a file input.
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("POST", '/pushfile', true);

xmlHttpRequest.setRequestHeader("Content-Type", file.type);

// Send the binary data.
// Since a File is a Blob, we can send it directly.
xmlHttpRequest.send(file);

브라우저 지원은 http://caniuse.com/#feat=xhr2(IE 10 이상을 포함한 대부분의 브라우저)를 참조하십시오 .


xmlHttpRequest.send (formData);
Li-chih Wu

7
마지막으로 FormData. 모두가 양식을 사용하는 것 같지만 필요한 것은 단일 파일 만 업로드하는 것입니다. 감사합니다!
Wilt 2015 년

나는 ajax를 통해 mp3 파일 업로드를 위해 이것을 작동시키는 방법을 몇 시간 동안 찾고 있었는데, 이것이 트릭을 수행합니다!
저스틴 빈센트

setRequestHeader는 formData를 전송하여 자동으로 설정되며 "Content-Type : multipart / form-data; boundary = ---- WebKitFormBoundaryQA8d7glpaso6zKsA"와 같이 표시되므로 setRequestHeader를 수행 할 필요가 없다고 생각합니다. 내가 setRequestHeader를 제거하지 않는 한 CORS.
저스틴 빈센트

참고 : 위의 내 의견은 formData 객체를 사용할 때만 적용됩니다.
저스틴 빈센트
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.