API Bahagian Belakang Perusahaan NestJS · Pelajaran

Menstrim Respons Besar dengan StreamableFile

Sediakan muat turun besar dengan cekap menggunakan StreamableFile dan strim boleh baca Node bagi mengelakkan penimbalan

Pelajaran 2 daripada 413 langkah

Menstrim Respons Besar dengan StreamableFile ialah pelajaran API Bahagian Belakang Perusahaan NestJS percuma di CoddyKit. Ini ialah pelajaran 2 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran API Bahagian Belakang Perusahaan NestJS, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus API Bahagian Belakang Perusahaan NestJS merangkumi sejumlah 4 pelajaran.

Masalah Penimbalan

Apabila pengawal mengembalikan tệp besar, pendekatan naif ialah membaca keseluruhan tệp ke dalam memori dan menghantarnya semula:

  • fs.readFileSync('huge.zip') memuatkan setiap bait ke dalam RAM sebelum bait pertama sampai kepada klien.
  • Eksport 2 GB yang disediakan kepada 50 pengguna serentak boleh menghabiskan timbunan dan merosakkan proses.

Penstriman menyelesaikan masalah ini: baca tệp dalam bahagian kecil dan paipkannya ke respons apabila bahagian itu tiba, sambil mengekalkan penggunaan memori pada tahap yang malar tanpa mengira saiz tệp.

Apakah StreamableFile

NestJS menyediakan kelas StreamableFile. Balut strim Node.js Readable (atau Buffer) di dalamnya dan kembalikannya daripada pengendali pengawal.

  • Nest mengesan nilai pulangan StreamableFile dan menyalurkan strim asas ke respons HTTP untuk anda.
  • Ia berfungsi pada kedua-dua penyesuai Express dan Fastify tanpa anda menyentuh res.pipe() secara manual.

Ini memastikan pengendali anda kekal deklaratif sambil masih menstrim bahagian demi bahagian.

import { Controller, Get, StreamableFile } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from 'path';

@Controller('files')
export class FilesController {
  @Get('report')
  getReport(): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'report.pdf'));
    return new StreamableFile(file);
  }
}

Cara Strim Readable Mengalir

Di sebalik tabir, createReadStream mengeluarkan acara 'data' dengan bahagian kecil Buffer (64 KB secara lalai). Respons menggunakan bahagian tersebut satu demi satu.

Berikut ialah demonstrasi Node asas tentang pembacaan berbahagian tanpa sebarang rangka kerja — perhatikan bahawa memori tidak pernah menyimpan keseluruhan fail pada satu-satu masa:

import { Readable } from 'stream';

// Simulate a large source as a stream of chunks
async function* generateChunks() {
  for (let i = 0; i < 5; i++) {
    yield `chunk-${i} `;
  }
}

const stream = Readable.from(generateChunks());

stream.on('data', (chunk: Buffer | string) => {
  console.log('received:', chunk.toString().trim());
});
stream.on('end', () => console.log('done streaming'));

Menetapkan Content-Type dan Nama Fail

Secara lalai, pelayar tidak mengetahui jenis strim tersebut. Hantarkan pilihan kepada StreamableFile supaya Nest menetapkan pengepala yang betul:

  • type menetapkan pengepala Content-Type.
  • disposition menetapkan Content-Disposition supaya pelayar memuat turun fail dengan nama fail, bukannya memaparkannya sebaris.
import { Controller, Get, StreamableFile } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from 'path';

@Controller('files')
export class FilesController {
  @Get('invoice')
  getInvoice(): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'invoice.pdf'));
    return new StreamableFile(file, {
      type: 'application/pdf',
      disposition: 'attachment; filename="invoice.pdf"',
    });
  }
}

Pengepala melalui @Header berbanding pilihan StreamableFile

Anda juga boleh menetapkan pengepala dengan penghias @Header(), tetapi penggunaan kedua-duanya serentak boleh menyebabkan konflik. Utamakan pilihan StreamableFile kerana Nest menggunakannya secara konsisten pada kedua-dua penyesuai.

  • Gunakan @Header('Content-Type', ...) hanya apabila nilainya statik dan diketahui semasa penyusunan.
  • Gunakan pilihan type/disposition bagi StreamableFile apabila nilainya dikira untuk setiap permintaan, contohnya nama fail dinamik.
import { Controller, Get, Header, StreamableFile } from '@nestjs/common';
import { createReadStream } from 'fs';

@Controller('exports')
export class ExportsController {
  @Get('static')
  @Header('Content-Type', 'text/csv')
  @Header('Content-Disposition', 'attachment; filename="data.csv"')
  download(): StreamableFile {
    return new StreamableFile(createReadStream('data.csv'));
  }
}

Menstrim Kandungan yang Dijana (tiada fail pada cakera)

StreamableFile tidak terhad kepada fail pada cakera. Sebarang Readable boleh digunakan — termasuk data yang dijana secara langsung. Ini amat sesuai untuk eksport CSV besar yang dibina baris demi baris daripada kursor pangkalan data.

Di bawah, penjana menghasilkan baris CSV secara malas supaya keseluruhan set data tidak pernah dimuatkan sepenuhnya ke dalam memori pada satu-satu masa.

import { Controller, Get, StreamableFile, Header } from '@nestjs/common';
import { Readable } from 'stream';

@Controller('exports')
export class CsvExportController {
  @Get('users.csv')
  @Header('Content-Type', 'text/csv')
  exportUsers(): StreamableFile {
    async function* rows() {
      yield 'id,name\n';
      for (let i = 1; i <= 100000; i++) {
        yield `${i},user_${i}\n`;
      }
    }
    return new StreamableFile(Readable.from(rows()));
  }
}

Tekanan Balik: Sebab Penstriman Kekal Selamat untuk Memori

Tekanan balik ialah mekanisme yang memastikan penstriman kekal selamat. Apabila klien atau rangkaian perlahan, bahagian boleh tulis memberi isyarat kepada bahagian boleh baca supaya berhenti seketika daripada menghasilkan bahagian data.

  • Pembacaan cakera yang pantas bersama klien yang perlahan tidak akan menyebabkan gigabait data terkumpul dalam memori.
  • pipe() Node (yang digunakan secara dalaman oleh Nest) mengendalikan perkara ini secara automatik — menjeda dan menyambung semula sumber.

Inilah sebabnya anda patut memulangkan strim dan bukannya Buffer gergasi.

import { Writable, Readable } from 'stream';

const source = Readable.from(['a', 'b', 'c', 'd', 'e']);

const slowSink = new Writable({
  write(chunk, _enc, cb) {
    console.log('wrote:', chunk.toString());
    setTimeout(cb, 10); // simulate slow consumer -> triggers backpressure
  },
});

source.pipe(slowSink);
slowSink.on('finish', () => console.log('all chunks flushed safely'));

Mengendalikan Ralat Strim

Jika strim asas mengalami ralat selepas pengepala dihantar, anda tidak lagi boleh menghantar isi ralat JSON. StreamableFile menyediakan pengendali ralat supaya anda boleh merekodkan ralat dan menutup sambungan dengan kemas.

  • Gunakan getStream().on('error', ...) atau pilihan pengendalian ralat setErrorHandler() untuk bertindak balas terhadap kegagalan pembacaan.
  • Tanpa ini, fail yang tiada boleh menyebabkan sambungan tergantung atau permintaan terhenti secara tidak dijangka.
import { Controller, Get, StreamableFile, Logger } from '@nestjs/common';
import { createReadStream } from 'fs';

@Controller('files')
export class SafeFilesController {
  private readonly logger = new Logger(SafeFilesController.name);

  @Get('archive')
  getArchive(): StreamableFile {
    const stream = createReadStream('archive.zip');
    const file = new StreamableFile(stream);
    file.setErrorHandler((err, response) => {
      this.logger.error(`Stream failed: ${err.message}`);
      response.statusCode = 404;
      response.end('File not available');
    });
    return file;
  }
}

Menstrim daripada Storan Objek (S3)

Dalam aplikasi perusahaan, fail biasanya berada dalam S3 atau storan objek lain, bukannya pada cakera setempat. SDK S3 memulangkan strim boleh baca untuk isi objek — hantarkannya terus kepada StreamableFile.

  • Tiada fail sementara dan tiada muat turun penuh ke dalam memori pelayan API.
  • Bait mengalir dari S3 → API anda → klien sebagai penyampai.
import { Controller, Get, Param, StreamableFile } from '@nestjs/common';
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { Readable } from 'stream';

@Controller('media')
export class MediaController {
  private s3 = new S3Client({ region: 'eu-central-1' });

  @Get(':key')
  async download(@Param('key') key: string): Promise<StreamableFile> {
    const obj = await this.s3.send(
      new GetObjectCommand({ Bucket: 'my-bucket', Key: key }),
    );
    return new StreamableFile(obj.Body as Readable);
  }
}

Mengakses Respons Mentah dengan @Res({ passthrough })

Kadang-kadang anda memerlukan objek respons mentah untuk menetapkan kod status atau pengepala tambahan, sambil masih membenarkan Nest menyalurkan StreamableFile. Gunakan @Res({ passthrough: true }) supaya Nest terus mengawal kitar hayat respons.

  • Tanpa passthrough: true, memasukkan @Res() menjadikan anda bertanggungjawab untuk menamatkan respons, dan pemulangan StreamableFile tidak lagi berfungsi secara automatik.
import { Controller, Get, Res, StreamableFile } from '@nestjs/common';
import type { Response } from 'express';
import { createReadStream, statSync } from 'fs';

@Controller('files')
export class RangeController {
  @Get('video')
  getVideo(@Res({ passthrough: true }) res: Response): StreamableFile {
    const { size } = statSync('movie.mp4');
    res.set({ 'Content-Length': size, 'Accept-Ranges': 'bytes' });
    return new StreamableFile(createReadStream('movie.mp4'));
  }
}

Mengubah Strim Secara Langsung

Anda boleh merangkaikan pengubah sebelum menyerahkan strim kepada StreamableFile. Kes biasa ialah memampatkan eksport besar dengan gzip supaya kurang data merentasi rangkaian — masih bahagian demi bahagian.

Berikut ialah demonstrasi kendiri tentang menyalurkan data melalui pengubah tanpa sebarang pelayan:

import { Readable, Transform } from 'stream';

const upper = new Transform({
  transform(chunk, _enc, cb) {
    cb(null, chunk.toString().toUpperCase());
  },
});

const source = Readable.from(['hello ', 'streamed ', 'world']);

source.pipe(upper).on('data', (c) => console.log(c.toString()));
upper.on('end', () => console.log('transform complete'));

Semakan Pantas

Uji pemahaman anda tentang keputusan teras di sebalik StreamableFile.

Ulang Kaji

Perkara penting untuk menstrim respons besar dalam NestJS:

  • Pulangkan new StreamableFile(readable) dan bukannya menyimpan keseluruhan fail dalam memori.
  • Bungkus sebarang Readable: createReadStream, isi objek S3 atau strim yang disokong penjana.
  • Tetapkan type dan disposition (atau @Header) supaya klien menerima jenis MIME dan nama fail muat turun yang betul.
  • Tekanan balik memastikan penggunaan memori kekal stabil apabila klien perlahan — inilah sebab utama penstriman digunakan.
  • Lampirkan setErrorHandler() untuk kegagalan pembacaan selepas pengepala dihantar.
  • Gunakan @Res({ passthrough: true }) apabila anda memerlukan akses kepada respons mentah sambil mengekalkan penyaluran Nest.
Percuma untuk bermula

Pelajari TypeScript dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
20
Pelajaran
76

Soalan Lazim

Adakah pelajaran “Menstrim Respons Besar dengan StreamableFile” percuma?

Ya — teks penuh “Menstrim Respons Besar dengan StreamableFile” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus API Bahagian Belakang Perusahaan NestJS, tingkat taraf kepada CoddyKit PRO. Kursus API Bahagian Belakang Perusahaan NestJS merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Menstrim Respons Besar dengan StreamableFile”?

Sediakan muat turun besar dengan cekap menggunakan StreamableFile dan strim boleh baca Node bagi mengelakkan penimbalan Anda berlatih API Bahagian Belakang Perusahaan NestJS menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan API Bahagian Belakang Perusahaan NestJS?

Tiada pengalaman terdahulu diperlukan. Pembelajaran API Bahagian Belakang Perusahaan NestJS di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 2 daripada 4.

Berapa lamakah pelajaran “Menstrim Respons Besar dengan StreamableFile” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran API Bahagian Belakang Perusahaan NestJS ini?

Ya. Setiap pelajaran API Bahagian Belakang Perusahaan NestJS menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Muat Naik Berbilang Bahagian dengan Pencegat Multer
  2. Menstrim Respons Besar dengan StreamableFile
  3. Muat Naik Terus ke S3 dengan URL Bertandatangan Awal
  4. Pipeline Pemprosesan Imej dengan Sharp
← Kembali ke API Bahagian Belakang Perusahaan NestJS