首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何使用aws浏览器(javascript)跟踪上传到S3的进度

如何使用aws浏览器(javascript)跟踪上传到S3的进度
EN

Stack Overflow用户
提问于 2021-01-14 23:20:32
回答 3查看 3.7K关注 0票数 9

我可以在网上找到很多资源,介绍如何使用aws S3 V2跟踪上传到V2的进度,并收听以下事件:

代码语言:javascript
复制
.on('httpUploadProgress', event => {}

但是自从我将aws更新到V3之后,就没有侦听器了。我认为我现在必须使用中间件功能,但是我尝试了一些东西,但它没有成功。我还深入了API参考文档github储存库,但没有成功。

我目前的代码如下:

代码语言:javascript
复制
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

export const UploadToS3 = (credentials, fileData) => {

    const s3 = new S3Client({
        region: credentials.region,
        credentials: {
            accessKeyId: credentials.access_key,
            secretAccessKey: credentials.secret_key,
            sessionToken: credentials.session_token,
        }
    });

    return new Promise((resolve) => {
        s3.send(new PutObjectCommand({
            Bucket: credentials.bucket,
            Key: credentials.file,
            Body: fileData,
        }));
    });
};

如能提供任何帮助,将不胜感激。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-03-12 15:30:07

我遇到了完全相同的问题(从aws v2切换到v3),发现这是因为库对所有的获取请求和Fetch 不支持跟踪上载进度都使用了获取 API。

为了解决这个问题,我用好的旧requestHandler交换了Fetch (至少用于PUT请求),这可以通过在初始化S3Client时提供一个自定义S3Client来实现。

代码语言:javascript
复制
import { S3Client } from '@aws-sdk/client-s3';

const myHttpHandler = new MyHttpHandler();
myHttpHandler.onProgress$.subscribe(progress => {
  const percentComplete = progress.progressEvent.loaded / progress.progressEvent.total * 100;
  console.log('upload progress', percentComplete);
});

const myClient = new S3Client({
  endpoint: this.configService.s3Api,
  region: 'eu',
  credentials: { ... },
  requestHandler: myHttpHandler
});

自定义请求处理程序只是从@aws/fetch-http-处理程序中扩展了FetchHttpHandler。如果方法是PUT,并且有一个主体(所以我们想上传一些东西),那么它使用一个自定义XHR处理程序-否则它只使用来自它的super类的Fetch处理程序。在XHR处理程序中,可以将某些东西绑定到XHR处理程序的progress事件--在我的示例中,我发出一个rxjs Subject,可以在自定义处理程序之外使用它。

代码语言:javascript
复制
import { FetchHttpHandler, FetchHttpHandlerOptions } from '@aws-sdk/fetch-http-handler';
import { HeaderBag, HttpHandlerOptions } from '@aws-sdk/types';
import { buildQueryString } from '@aws-sdk/querystring-builder';
import { HttpResponse, HttpRequest } from '@aws-sdk/protocol-http';
import { Subject } from 'rxjs';

class MyHttpHandler extends FetchHttpHandler {
  private myRequestTimeout;

  onProgress$: Subject<{ path: string, progressEvent: ProgressEvent }> = new Subject();

  constructor({ requestTimeout }: FetchHttpHandlerOptions = {}) {
    super({ requestTimeout });
    this.myRequestTimeout = requestTimeout;
  }

  handle(request: HttpRequest, { abortSignal }: HttpHandlerOptions = {}): Promise<{ response: HttpResponse }> {
    // we let XHR only handle PUT requests with body (as we want to have progress events here), the rest by fetch
    if (request.method === 'PUT' && request.body) {
      return this.handleByXhr(request, { abortSignal });
    }
    return super.handle(request, { abortSignal });
  }

  /**
   * handles a request by XHR instead of fetch
   * this is a copy the `handle` method of the `FetchHttpHandler` class of @aws-sdk/fetch-http-handler
   * replacing the `Fetch`part with XHR
   */
  private handleByXhr(request: HttpRequest, { abortSignal }: HttpHandlerOptions = {}): Promise<{ response: HttpResponse}> {
    const requestTimeoutInMs = this.myRequestTimeout;

    // if the request was already aborted, prevent doing extra work
    if (abortSignal?.aborted) {
      const abortError = new Error('Request aborted');
      abortError.name = 'AbortError';
      return Promise.reject(abortError);
    }

    let path = request.path;
    if (request.query) {
      const queryString = buildQueryString(request.query);
      if (queryString) {
        path += `?${queryString}`;
      }
    }

    const { port, method } = request;
    const url = `${request.protocol}//${request.hostname}${port ? `:${port}` : ''}${path}`;
    // Request constructor doesn't allow GET/HEAD request with body
    // ref: https://github.com/whatwg/fetch/issues/551
    const body = method === 'GET' || method === 'HEAD' ? undefined : request.body;
    const requestOptions: RequestInit = {
      body,
      headers: new Headers(request.headers),
      method,
    };


    const myXHR = new XMLHttpRequest();
    const xhrPromise = new Promise<{headers: string[], body: Blob, status: number}>((resolve, reject) => {
      try {
        myXHR.responseType = 'blob';

        // bind the events
        myXHR.onload = progressEvent => {
          resolve({
            body: myXHR.response,
            headers: myXHR.getAllResponseHeaders().split('\n'),
            status: myXHR.status
          });
        };
        myXHR.onerror = progressEvent => reject(new Error(myXHR.responseText));
        myXHR.onabort = progressEvent => {
          const abortError = new Error('Request aborted');
          abortError.name = 'AbortError';
          reject(abortError);
        };

        // progress event musst be bound to the `upload` property
        if (myXHR.upload) {
          myXHR.upload.onprogress = progressEvent => this.onProgress$.next({ path, progressEvent });
        }


        myXHR.open(requestOptions.method, url);
        // append headers
        if (requestOptions.headers) {
          (requestOptions.headers as Headers).forEach((headerVal, headerKey, headers) => {
            if (['host', 'content-length'].indexOf(headerKey.toLowerCase()) >= 0) {
              // avoid "refused to set unsafe header" error message
              return;
            }

            myXHR.setRequestHeader(headerKey, headerVal);
          });
        }
        myXHR.send(requestOptions.body);
      } catch (e) {
        console.error('S3 XHRHandler error', e);
        reject(e);
      }
    });

    const raceOfPromises = [
      xhrPromise.then((response) => {
        const fetchHeaders = response.headers;
        const transformedHeaders: HeaderBag = {};

        fetchHeaders.forEach(header => {
          const name = header.substr(0, header.indexOf(':') + 1);
          const val =  header.substr(header.indexOf(':') + 1);
          if (name && val) {
            transformedHeaders[name] = val;
          }
        });

        const hasReadableStream = response.body !== undefined;

        // Return the response with buffered body
        if (!hasReadableStream) {
          return response.body.text().then(body => ({
            response: new HttpResponse({
              headers: transformedHeaders,
              statusCode: response.status,
              body,
            }),
          }));
        }
        // Return the response with streaming body
        return {
          response: new HttpResponse({
            headers: transformedHeaders,
            statusCode: response.status,
            body: response.body,
          }),
        };
      }),
      this.requestTimeoutFn(requestTimeoutInMs),
    ];
    if (abortSignal) {
      raceOfPromises.push(
        new Promise<never>((resolve, reject) => {
          abortSignal.onabort = () => {
            myXHR.abort();
          };
        })
      );
    }
    return Promise.race(raceOfPromises);
  }

  private requestTimeoutFn(timeoutInMs = 0): Promise<never> {
    return new Promise((resolve, reject) => {
      if (timeoutInMs) {
        setTimeout(() => {
          const timeoutError = new Error(`Request did not complete within ${timeoutInMs} ms`);
          timeoutError.name = 'TimeoutError';
          reject(timeoutError);
        }, timeoutInMs);
      }
    });
  }
}
票数 7
EN

Stack Overflow用户

发布于 2021-01-15 14:31:25

纵观github问题,我刚刚发现@aws-sdk/client-s3不支持上传进度跟踪,因为它在封面下使用fetchHttpHandler。推荐的方法是使用@aws-sdk/lib-storage,我还没有尝试过,但看起来很有希望!

票数 2
EN

Stack Overflow用户

发布于 2022-09-02 13:44:57

我也面临着同样的问题,那就是我必须将aws从v2升级到v3,但是在v3中缺少了文件上传进度特性。原因是在JS中,v2 for S3文件上传时使用XHR进行浏览器网络请求,具有良好的进度跟踪接口。JS v3也使用了fetch,这提供了一些其他的优点,但缺点之一是它似乎还不支持上传进度,而且这个特性的进展非常缓慢。

但是现在JS v3有了一个名为@aws/xhr-http-处理程序的新包,它可以代替@aws/fetch处理程序来获得细粒度的文件上传进度,就像我们在v2中看到的那样。

您可以在链接https://github.com/aws/aws-sdk-js-v3/tree/main/packages/xhr-http-handler上找到相关代码。

代码语言:javascript
复制
       import { S3Client } from '@aws-sdk/client-s3';
       import { XhrHttpHandler } from '@aws-sdk/xhr-http-handler';
       import { Upload } from '@aws-sdk/lib-storage';
        
         const s3Client = new S3Client({
          requestHandler: new XhrHttpHandler({}),
        }); 
        const upload = new Upload({
                        client:s3Client,
                        params: {
                           bucket,
                           key,
                         },
                     });
upload.on("httpUploadProgress", (progress) => {
  console.log(
    progress.loaded, // Bytes uploaded so far.
    progress.total // Total bytes. Divide these two for progress percentage.
  );
});
await upload.done();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65728325

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档