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

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

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

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

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

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

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


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



답변

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

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

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

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

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


답변

내 해결책은 이것을 매우 잘 지원 하는 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 에서 반응에 이것을 사용하는 예가 있습니다.


답변

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

요청 진행 과 관련하여 현재 [ 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 사양 은 아직 완성되지 않은 것으로 보이며이 기능 이 이미 모든 가져 오기 구현에서 작동하는지 여부는 알 수 없습니다.


답변

업데이트 : 받아 들여진 대답에 따르면 지금은 불가능합니다. 그러나 아래 코드는 언젠가 문제를 처리했습니다. 최소한 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/


답변

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

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


답변

가능한 해결 방법은 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));

}


답변

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}%`;
}