가져올 진행률 표시기를 업로드 하시겠습니까?


99

fetch를 사용하여 업로드 진행률 표시기를 구현하는 문서 또는 예제를 찾는 데 어려움을 겪고 있습니다 .

이것은 내가 지금까지 찾은 유일한 참조 이며 다음과 같습니다.

진행 이벤트는 지금은 가져 오지 않는 고급 기능입니다. Content-Length헤더 를보고 통과 스트림을 사용하여 수신 된 바이트를 모니터링 하여 직접 만들 수 있습니다 .

즉, Content-Length다르게 하지 않고 명시 적으로 응답을 처리 할 수 ​​있습니다 . 물론 Content-Length거기 있어도 거짓말이 될 수 있습니다. 스트림을 사용하면 이러한 거짓말을 원하는대로 처리 할 수 ​​있습니다.

전송 된 "바이트를 모니터링하기위한 통과 스트림"을 어떻게 작성합니까? 어떤 종류의 차이가 있다면 브라우저에서 Cloudinary로 이미지 업로드를 강화 하기 위해이 작업을 수행하려고합니다 .

참고 : Cloudinary JS 라이브러리 에는 관심 이 없습니다. jQuery에 의존하고 내 앱에는 그렇지 않기 때문입니다. 네이티브 자바 스크립트와 Github의 폴리 필로 이를 수행하는 데 필요한 스트림 처리에만 관심이 있습니다.fetch


https://fetch.spec.whatwg.org/#fetch-api


답변:


44

스트림이 웹 플랫폼 ( https://jakearchibald.com/2016/streams-ftw/ ) 에 착륙하기 시작 했지만 아직 초기 단계입니다.

곧 요청 본문으로 스트림을 제공 할 수있게 될 것이지만 공개 된 질문은 해당 스트림의 소비가 업로드 된 바이트와 관련이 있는지 여부입니다.

특정 리디렉션으로 인해 데이터가 새 위치로 재전송 될 수 있지만 스트림은 "다시 시작"할 수 없습니다. 본문을 여러 번 호출 할 수있는 콜백으로 변환하여이 문제를 해결할 수 있지만 리디렉션 수를 노출하는 것이 보안 누출이 아닌지 확인해야합니다. JS 플랫폼에서는 처음 일 수 있기 때문입니다. 그것을 감지하십시오.

일부는 스트림 소비를 업로드 된 바이트에 연결하는 것이 합리적 일지 의문을 제기합니다.

간단히 말해서 : 아직 불가능하지만 앞으로는 스트림이나 .NET에 전달 된 일종의 상위 수준 콜백에 의해 처리 될 것 fetch()입니다.


7
안 됐네요. 당장은 받아들이지 만 이것이 현실이되면 다른 누군가가 업데이트 된 솔루션을 게시하기를 바랍니다! :)
neezer

1
업데이트-스트림을 사용하여 API 가져 오기 진행 상황 표시 -twitter.com/umaar/status/917789464658890753/photo/1
Eitan Peer

2
@EitanPeer 좋네요. 비슷한 것이 업로드에도 작동합니까, 예 : POST?
Michael

4
@EitanPeer 그러나이 문제는 없습니다 다운로드에, 업로드 진행에 관한 것입니다
존 Balvin 아리아

1
지금은 2020 년입니다. 아직 이렇게 할 방법이없는 이유는 무엇입니까? (
MHA15

23

내 해결책은 이것을 매우 잘 지원 하는 axios 를 사용 하는 것입니다.

      axios.request( {
        method: "post", 
        url: "/aaa", 
        data: myData, 
        onUploadProgress: (p) => {
          console.log(p); 
          //this.setState({
            //fileprogress: p.loaded / p.total
          //})
        }


      }).then (data => {
        //this.setState({
          //fileprogress: 1.0,
        //})
      })

github 에서 반응에 이것을 사용하는 예가 있습니다.


2
그게 내 해결책이었습니다. Axios는 금형에 정말 잘 맞는 것 같습니다.
Jason Rice

1
또는 후드 아래 에서 axios사용 합니까 ? fetchXMLHttpRequest
Dai

3
XMLHttpRequest. React Native에 이것을 사용하는 경우 XMLHttpRequest가 가져 오기와 비교할 때 큰 json 응답을 구문 분석하는 데 매우 느리다는 점에 유의하십시오 (약 10 배 더 느리고 전체 UI 스레드가 고정됨).
Cristiano Coelho

21
질문에 대답하지 않습니다! 질문이 "y에서 x를 어떻게합니까?"인 경우 "대신 z에서 x를 수행하십시오"라고 말하는 것은 허용되는 대답이 아닙니다.
Derek Henderson

3
이것은 특히 후드 아래에서 axios사용하지 않고 fetch그러한 지원이 없기 때문에 질문에 대답 하지 않습니다 . 나는 말 그대로 그들을 위해 지금 그것을 작성하고 있습니다 .
sgammon

7

나는 그것이 가능하다고 생각하지 않는다. 초안은 다음과 같이 설명합니다.

요청 진행 과 관련하여 현재 [ XHR에 비해 ] 부족합니다.


(이전 답변) : Fetch API 장의
첫 번째 예는 다음 방법에 대한 통찰력을 제공합니다.

신체 데이터를 점진적으로 수신하려는 경우 :

function consume(reader) {
  var total = 0
  return new Promise((resolve, reject) => {
    function pump() {
      reader.read().then(({done, value}) => {
        if (done) {
          resolve()
          return
        }
        total += value.byteLength
        log(`received ${value.byteLength} bytes (${total} bytes in total)`)
        pump()
      }).catch(reject)
    }
    pump()
  })
}

fetch("/music/pk/altes-kamuffel.flac")
  .then(res => consume(res.body.getReader()))
  .then(() => log("consumed the entire body without keeping the whole thing in memory!"))
  .catch(e => log("something went wrong: " + e))

그렇다의 사용에서 Promise생성자 안티 패턴 , 당신은 볼 수 response.body당신이 리더를 사용하여 바이트로 바이트를 읽을 수있는 Stream에, 당신은 이벤트를 발생 또는 같은 당신이 무엇을 그들 모두를 위해 (예 : 진행 상황을 기록) 할 수 있습니다.

그러나 Streams 사양 은 아직 완성되지 않은 것으로 보이며이 기능 이 이미 모든 가져 오기 구현에서 작동하는지 여부는 알 수 없습니다.


11
내가 제대로 그 예를 읽는다면,하지만,이에 대한 것 다운로드 를 통해 파일을 fetch. 파일 업로드 진행률 표시기에 관심 이 있습니다.
neezer

죄송합니다. 그 인용구는 바이트 수신 에 대해 말하고 있는데, 이는 저를 혼란스럽게합니다.
Bergi

@Bergi 참고, Promise생성자는 필요하지 않습니다. Response.body.getReader()를 반환합니다 Promise. 큰 크기의 json을 다운로드 할 때 Uncaught RangeError를 해결하는 방법을
guest271314 dec.

3
@ guest271314 예, 이미 견적 의 출처에서 수정했습니다 . 그리고 아니오, getReader약속을 반환하지 않습니다. 이것이 당신이 링크 한 포스트와 무슨 관련이 있는지 모릅니다.
Bergi

예 @Bergi, 당신이 올바른지 확인 .getReader().read()방법은을 반환합니다 Promise. 그것이 전달하려는 것입니다. 링크는 다운로드 진행 상황을 확인할 수 있으면 업로드 진행 상황을 확인할 수 있다는 전제를 암시하는 것입니다. 예상되는 결과를 상당한 정도로 반환하는 패턴을 모으십시오. fetch()업로드 진행입니다 . A와 방법을 발견하지 않았습니다 또는 아마 뭔가 간단한 누락, jsfiddle에서 개체를. 네트워크 상태를 모방하지 않고 업로드 파일을 매우 빠르게 테스트 합니다. 기억하지만 . echoBlobFilelocalhostNetwork throttling
guest271314

6

업데이트 : 받아 들여진 대답에 따르면 지금은 불가능합니다. 그러나 아래 코드는 언젠가 문제를 처리했습니다. 최소한 XMLHttpRequest를 기반으로하는 라이브러리를 사용하도록 전환해야한다는 점을 추가해야합니다.

const response = await fetch(url);
const total = Number(response.headers.get('content-length'));

const reader = response.body.getReader();
let bytesReceived = 0;
while (true) {
    const result = await reader.read();
    if (result.done) {
        console.log('Fetch complete');
        break;
    }
    bytesReceived += result.value.length;
    console.log('Received', bytesReceived, 'bytes of data so far');
}

이 링크 덕분에 : https://jakearchibald.com/2016/streams-ftw/


2
좋지만 업로드에도 적용됩니까?
커널

@kernel 알아 내려고했지만 할 수 없었습니다. 업로드를 위해이 작업을 수행하는 방법도 찾고 싶습니다.
Hosseinmp76

동일하지만 지금까지 작동하는 업로드 예제를 찾고 생성하는 것은 운이 좋지 않았습니다.
커널

1
content-length! == 몸의 길이. http 압축을 사용하는 경우 (대용량 다운로드에 일반적 임) content-length는 http 압축 후의 크기이고 길이는 파일이 추출 된 후의 크기입니다.
Ferrybig

@Ferrybig 나는 당신의 요점을 이해하지 못했습니다. 어딘가에서 평등을 사용 했습니까?
Hosseinmp76

4

답변 중 어느 것도 문제를 해결하지 못하기 때문입니다.

구현을 위해 알려진 크기의 작은 초기 청크로 업로드 속도 감지 할 수 있으며 업로드 시간은 content-length / upload-speed로 계산할 수 있습니다. 이 시간을 추정치로 사용할 수 있습니다.


3
사용하기 매우 영리한, 멋진 트릭 우리가 : 실시간 솔루션을 기다리는 동안
매직스

13
나에게는 너무 위험합니다. 싶지 않을 것이다 창문 파일 진행률 표시 줄을 복사처럼 끝
잭 Giffin

2

가능한 해결 방법은 new Request()생성자 를 활용 한 다음 Request.bodyUsed Boolean속성 을 확인하는 것입니다.

bodyUsed속성의 게터는 경우 true를 반환해야한다 disturbed, 그렇지 않으면 거짓.

스트림인지 확인하려면 distributed

Body믹스 인을 구현하는 객체 는 disturbedif bodyis non-null이고 streamis라고 disturbed합니다.

이 같을 때 의 재귀 호출에 연결된 fetch() Promise내부에서를 반환합니다 ..then().read()ReadableStreamRequest.bodyUsedtrue

이 접근 방식은 Request.body바이트가 끝점으로 스트리밍되므로의 바이트를 읽지 않습니다 . 또한 브라우저에 응답이 모두 반환되기 전에 업로드가 완료 될 수 있습니다.

const [input, progress, label] = [
  document.querySelector("input")
  , document.querySelector("progress")
  , document.querySelector("label")
];

const url = "/path/to/server/";

input.onmousedown = () => {
  label.innerHTML = "";
  progress.value = "0"
};

input.onchange = (event) => {

  const file = event.target.files[0];
  const filename = file.name;
  progress.max = file.size;

  const request = new Request(url, {
    method: "POST",
    body: file,
    cache: "no-store"
  });

  const upload = settings => fetch(settings);

  const uploadProgress = new ReadableStream({
    start(controller) {
        console.log("starting upload, request.bodyUsed:", request.bodyUsed);
        controller.enqueue(request.bodyUsed);
    },
    pull(controller) {
      if (request.bodyUsed) {
        controller.close();
      }
      controller.enqueue(request.bodyUsed);
      console.log("pull, request.bodyUsed:", request.bodyUsed);
    },
    cancel(reason) {
      console.log(reason);
    }
  });

  const [fileUpload, reader] = [
    upload(request)
    .catch(e => {
      reader.cancel();
      throw e
    })
    , uploadProgress.getReader()
  ];

  const processUploadRequest = ({value, done}) => {
    if (value || done) {
      console.log("upload complete, request.bodyUsed:", request.bodyUsed);
      // set `progress.value` to `progress.max` here 
      // if not awaiting server response
      // progress.value = progress.max;
      return reader.closed.then(() => fileUpload);
    }
    console.log("upload progress:", value);
    progress.value = +progress.value + 1;
    return reader.read().then(result => processUploadRequest(result));
  };

  reader.read().then(({value, done}) => processUploadRequest({value,done}))
  .then(response => response.text())
  .then(text => {
    console.log("response:", text);
    progress.value = progress.max;
    input.value = "";
  })
  .catch(err => console.log("upload error:", err));

}

-3
const req = await fetch('./foo.json');
const total = Number(req.headers.get('content-length'));
let loaded = 0;
for await(const {length} of req.body.getReader()) {
  loaded = += length;
  const progress = ((loaded / total) * 100).toFixed(2); // toFixed(2) means two digits after floating point
  console.log(`${progress}%`); // or yourDiv.textContent = `${progress}%`;
}

전체 답변에 대해 Benjamin Gruenbaum에게 감사를 표하고 싶습니다. 그의 강의에서 배웠기 때문입니다.
Leon Gilyadov

@LeonGilyadov 강의는 어디서나 온라인으로 볼 수 있습니까? 소스에 대한 링크가 좋을 것입니다.
Mark Amery

: 그것은 여기 @MarkAmery youtube.com/watch?v=Ja8GKkxahCo (강의가 히브리어 주어)
레온 Gilyadov

11
문제는 다운로드가 아니라 업로드에 관한 것입니다.
sarneeh

가져 오기 진행의 문제는 업로드 할 때입니다 (다운로드에는 문제 없음)
Kamil Kiełczewski

-4

핵심 부분은 ReadableStream입니다.obj_response .body≫입니다.

견본:

let parse=_/*result*/=>{
  console.log(_)
  //...
  return /*cont?*/_.value?true:false
}

fetch('').
then(_=>( a/*!*/=_.body.getReader(), b/*!*/=z=>a.read().then(parse).then(_=>(_?b:z=>z)()), b() ))

https://html.spec.whatwg.org/https://html.spec.whatwg.org/print.pdf 와 같은 거대한 페이지에서 실행을 테스트 할 수 있습니다. . CtrlShiftJ를 누르고 코드를로드합니다.

(Chrome에서 테스트되었습니다.)


이 답변은 마이너스 포인트를 받지만 아무도 마이너스 포인트를 제공하는 이유를 설명하지 않습니다. 그래서 나는 +1을 제공합니다
Kamil Kiełczewski

3
그것은 업로드 와 관련이 없기 때문에 나로부터 -1을 얻습니다 .
Brad
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.