Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/370.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用aws sdk V3 for browser(javascript)跟踪到S3的上传进度_Javascript_Amazon Web Services_Amazon S3_Aws Sdk - Fatal编程技术网

如何使用aws sdk V3 for browser(javascript)跟踪到S3的上传进度

如何使用aws sdk V3 for browser(javascript)跟踪到S3的上传进度,javascript,amazon-web-services,amazon-s3,aws-sdk,Javascript,Amazon Web Services,Amazon S3,Aws Sdk,我可以在网上找到很多关于如何使用aws sdk V2跟踪S3上传进度的资源,比如: .on('httpUploadProgress', event => {} 但是自从我将aws sdk更新为V3之后,就没有监听器了。我相信我现在必须使用中间件功能,但我已经尝试了一些方法,但没有成功。我也深入研究了这个问题,但没有成功 我当前的代码如下: import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; export co

我可以在网上找到很多关于如何使用aws sdk V2跟踪S3上传进度的资源,比如:

.on('httpUploadProgress', event => {}
但是自从我将aws sdk更新为V3之后,就没有监听器了。我相信我现在必须使用中间件功能,但我已经尝试了一些方法,但没有成功。我也深入研究了这个问题,但没有成功

我当前的代码如下:

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,
        }));
    });
};

如果有任何帮助,我们将不胜感激。

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

我遇到了完全相同的问题(从aws sdk v2切换到v3),并发现这是因为库对所有HTTP请求和
Fetch

为了解决这个问题,我至少为
PUT
请求交换了
Fetch
好的
XMLHttpRequest
,这可以通过在初始化S3Client时提供一个定制的requestHandler来实现

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 sdk/fetch http处理程序扩展了
FetchHttpHandler
。如果方法是
PUT
,并且有一个主体(因此我们想上传一些东西),那么它使用一个自定义的XHR处理程序——否则它只使用它的
super
类中的
Fetch
处理程序。 在XHR处理程序中,您可以将某些内容绑定到XHR处理程序的
progress
事件-在我的例子中,我发出一个rxjs
Subject
,我可以在自定义处理程序之外使用它

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);
      }
    });
  }
}

import{FetchHttpHandler,FetchHttpHandlerOptions}来自'@aws sdk/fetch http handler';
从“@aws sdk/types”导入{HeaderBag,HttpHandlerOptions};
从'@aws sdk/querystring builder'导入{buildQueryString};
从'@aws sdk/protocol http'导入{HttpResponse,HttpRequest};
从'rxjs'导入{Subject};
类MyHttpHandler扩展了FetchHttpHandler{
私有请求超时;
onProgress$:主题=新主题();
构造函数({requestTimeout}:FetchHttpHandlerOptions={}){
超级({requestTimeout});
this.myRequestTimeout=requestTimeout;
}
句柄(请求:HttpRequest,{abortSignal}:HttpHandlerOptions={}):承诺{
//我们让XHR只处理带有主体的PUT请求(因为我们希望这里有进度事件),其余的通过fetch处理
if(request.method==='PUT'&&request.body){
返回此.handlebyshr(请求,{abortSignal});
}
返回super.handle(请求,{abortSignal});
}
/**
*通过XHR而不是fetch处理请求
*这是@aws sdk/fetch http handler的`FetchHttpHandler`类的`handle`方法的副本
*用XHR替换“Fetch”部分
*/
私有handleyxhr(请求:HttpRequest,{abortSignal}:HttpHandlerOptions={}):承诺{
const requesttimeoutims=this.myRequestTimeout;
//如果请求已中止,请阻止执行额外的工作
如果(中止信号?.中止){
const abortorror=新错误(“请求已中止”);
abortorror.name='abortorror';
返回承诺。拒绝(中止);
}
让path=request.path;
if(request.query){
const queryString=buildQueryString(request.query);
如果(查询字符串){
路径+=`?${queryString}`;
}
}
const{port,method}=request;
常量url=`${request.protocol}/${request.hostname}${port?`:${port}`:''}${path}`;
//请求构造函数不允许带有主体的GET/HEAD请求
//参考:https://github.com/whatwg/fetch/issues/551
const body=method=='GET'| | method=='HEAD'?未定义:request.body;
常量requestOptions:RequestInit={
身体,
标题:新标题(request.headers),
方法,,
};
const myXHR=new XMLHttpRequest();
const xhrPromise=新承诺((解决、拒绝)=>{
试一试{
myXHR.responseType='blob';
//约束事件
myXHR.onload=progressEvent=>{
决心({
正文:myXHR.response,
标题:myXHR.getAllResponseHeaders().split('\n'),
状态:myXHR.status
});
};
myXHR.onerror=progressEvent=>reject(新错误(myXHR.responseText));
myXHR.onabort=progressEvent=>{
const abortorror=新错误(“请求已中止”);
abortorror.name='abortorror';
拒绝(恐怖);
};
//无法将进度事件绑定到“上载”属性
if(myXHR.upload){
myXHR.upload.onprogress=progressEvent=>this.onprogress$.next({path,progressEvent});
}
myXHR.open(requestOptions.method,url);
//附加标题
if(requestOptions.headers){
(requestOptions.headers作为头)。forEach((headerVal,headerKey,headers)=>{
if(['host','content length'].indexOf(headerKey.toLowerCase())>=0){
//避免“拒绝设置不安全的标头”错误消息
返回;
}
myXHR.setRequestHeader(headerKey,headerVal);
});
}
myXHR.send(requestOptions.body);
}捕获(e){
console.error('s3xhrhandler error',e);
拒绝(e);
}
});
概率常数=[
xhromise.then((响应)=>{
const fetchHeaders=response.headers;
const transformedHeaders:HeaderBag={};
fetchHeaders.forEach(头=>{
const name=header.substr(0,header.indexOf(“:”)+1);
const val=header.substr(header.indexOf(':')+1);
如果(名称和值){
transformedHeaders[name]=val;
}
});
const hasReadableStream=response.body!==未定义;
//返回带有缓冲体的响应
如果(!hasReadableStream)