【问题标题】:AWS-SDK v3 - Download file with TypescriptAWS-SDK v3 - 使用 Typescript 下载文件
【发布时间】:2021-09-23 03:41:28
【问题描述】:

编写基于 Typescript 的 Lambda 以从 S3 存储桶下载文件进行处理,但在使用 AWS SDK v3 时遇到问题。

我收到错误“类型'Readable | ReadableStream | Blob'上不存在属性'pipe'。类型'ReadableStream'上不存在属性'pipe'。”

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";

const s3Client = new S3Client();        
const command = new GetObjectCommand({ Bucket: myBucket, Key: myFile });

const s3Item = await s3Client.send(command);
s3Item.Body.pipe(createWriteStream(fileName));

已尝试使用 .pipeTo 和 .pipeThrough,但它们有效地报告了相同的错误。

对于为什么这不起作用或如何通过 SDK 的 v3 实现下载有什么想法吗?

我很难找到下载示例,甚至官方 AWS 文档也包含上传和其他步骤,但没有下载。 AWS S3 Examples

【问题讨论】:

  • 这是运行时问题还是转译时间问题?看起来你正在使用 TypeScript,所以我认为这是一个转换时间问题......如果你确定你会收到一个 ReadableStream 试试const body = s3Item.Body as ReadableStream

标签: typescript amazon-web-services amazon-s3 aws-sdk


【解决方案1】:

Body 的类型是一个相当笨拙的Readable | ReadableStream | Blob | undefined。这是因为该库可在节点和浏览器中使用,但不幸的是只是将它们全部塞进了一个定义中。

  • 如果使用节点,它将是Readable
  • 在现代浏览器中,它将是ReadableStream
  • 在旧浏览器中,它将是Blob

我没有发现Bodyundefined的情况,但是在源码里翻了一下,貌似是底层http层的神器。到目前为止,只要假设它已定义,我就很好,这使得辅助方法的签名更加方便。但是,在某些情况下,假设我没有遇到过可能是不安全的。

因此,对于节点,您可以使用如下内容:

import { Readable } from 'stream';
import { GetObjectCommandOutput } from '@aws-sdk/client-s3';

export const asStream = (response: GetObjectCommandOutput) => {
  return response.Body as Readable;
};

export const asBuffer = async (response: GetObjectCommandOutput) => {
  const stream = asStream(response);
  const chunks: Buffer[] = [];
  return new Promise<Buffer>((resolve, reject) => {
    stream.on('data', (chunk) => chunks.push(chunk));
    stream.on('error', (err) => reject(err));
    stream.on('end', () => resolve(Buffer.concat(chunks)));
  });
};

export const asString = async (response: GetObjectCommandOutput) => {
  const buffer = await asBuffer(response);
  return buffer.toString();
};

或者如果你想对undefined更严格:

const getBody = (response: GetObjectCommandOutput) => {
  return response.Body && (response.Body as Readable);
};

const getBodyAsBuffer = async (response: GetObjectCommandOutput) => {
  const stream = getBody(response);
  if (stream) {
    const chunks: Buffer[] = [];
    return new Promise<Buffer>((resolve, reject) => {
      stream.on('data', (chunk) => chunks.push(chunk));
      stream.on('error', (err) => reject(err));
      stream.on('end', () => resolve(Buffer.concat(chunks)));
    });
  }
};

const getBodyAsString = async (response: GetObjectCommandOutput) => {
  const buffer = await getBodyAsBuffer(response);
  return buffer?.toString();
};

在浏览器中,以下内容应该适用于现代浏览器,但如果您的应用支持 IE,请务必注意此处的兼容性,因为这不处理旧版浏览器 Blob 响应:https://developer.mozilla.org/en-US/docs/Web/API/Streams_API#browser_compatibility

import { GetObjectCommandOutput } from '@aws-sdk/client-s3';

export const asStream = (s3Response: GetObjectCommandOutput) => {
  return s3Response.Body as ReadableStream;
};

export const asBlob = async (response: GetObjectCommandOutput) => {
  return await new Response(asStream(response)).blob();
};

export const asString = async (response: GetObjectCommandOutput) => {
  return await new Response(asStream(response)).text();
};

我认为从长远来看,@aws-sdk/lib-storage 将需要一个类似于UploadDownload,但到目前为止还没有听说它有任何积压。请参阅此 GitHub 问题以进行长期讨论:https://github.com/aws/aws-sdk-js-v3/issues/1877

【讨论】:

    【解决方案2】:

    显然,对于节点,s3Item.Body 是可读的。在浏览器中,它是 ReadableStream 或 Blob(参见 https://github.com/aws/aws-sdk-js-v3/issues/1877)。

    import {
      S3Client,
      GetObjectCommand,
      GetObjectCommandOutput
    } from '@aws-sdk/client-s3';
    import internal from 'stream';
    
    const s3Client = new S3Client();        
    const command = new GetObjectCommand({
      Bucket: myBucket,
      Key: myFile
    });
    const s3Item = await s3Client.send(command);
    const commandResult: GetObjectCommandOutput =
      await s3Client.send(new GetObjectCommand(getObjectParams));
    
    if (commandResult.Body instanceof internal.Readable) {
      let readableStream: internal.Readable =
        commandResult.Body as internal.Readable;
      readableStream = readableStream.pipe(...);
    } else {
      console.log(`GetObjectCommand should return an
        internal.Readable object. Maybe the code is
        running in the Browser?`);
    }

    【讨论】:

      猜你喜欢
      • 2021-07-17
      • 2023-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-17
      • 1970-01-01
      • 2013-06-08
      • 1970-01-01
      相关资源
      最近更新 更多