Suurten vastausten suoratoisto StreamableFilella
Tarjoa suuret lataukset tehokkaasti StreamableFilen ja Noden luettavien virtojen avulla ilman puskurointia.
Suurten vastausten suoratoisto StreamableFilella on ilmainen NestJS-yritysbackendien API:t-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu NestJS-yritysbackendien API:t-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. NestJS-yritysbackendien API:t-kurssilla on yhteensä 4 oppituntia.
Puskurointiongelma
Kun ohjain palauttaa suuren tiedoston, naiivi lähestymistapa on lukea koko tiedosto muistiin ja lähettää se takaisin:
fs.readFileSync('huge.zip')lataa jokaisen tavun RAM-muistiin ennen kuin ensimmäinen tavu saavuttaa asiakkaan.- 50 samanaikaiselle käyttäjälle tarjottu 2 GB:n vientitiedosto voi kuluttaa keon loppuun ja kaataa prosessin.
Suoratoisto ratkaisee ongelman: tiedosto luetaan pieninä paloina ja putkitetaan vastaukseen niiden saapuessa, jolloin muistin käyttö pysyy tasaisena tiedoston koosta riippumatta.
Mikä StreamableFile on
NestJS sisältää StreamableFile-luokan. Siihen kääritään Node.js:n Readable-tietovirta (tai Buffer), minkä jälkeen se palautetaan ohjaimen käsittelijästä.
- Nest tunnistaa palautetun
StreamableFile-arvon ja putkittaa taustalla olevan tietovirran HTTP-vastaukseen puolestanne. - Se toimii sekä Express- että Fastify-sovittimilla ilman, että käsittelette
res.pipe()-kutsua itse.
Näin käsittelijä pysyy deklaratiivisena, vaikka tiedosto suoratoistetaan edelleen pala kerrallaan.
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);
}
}Miten Readable-tietovirta etenee
Kulissien takana createReadStream lähettää 'data'-tapahtumia pieninä Buffer-paloina (oletuksena 64 kt). Vastaus käsittelee ne yksi kerrallaan.
Tässä on pelkällä Nodella tehty esimerkki paloittaisesta lukemisesta ilman frameworkia — huomaa, ettei koko tiedosto ole koskaan kerralla muistissa:
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'));Content-Typen ja tiedostonimen määrittäminen
Oletusarvoisesti selain ei tiedä, mitä streami sisältää. Välitä asetukset StreamableFile-oliolle, jotta Nest määrittää oikeat otsakkeet:
typemäärittääContent-Type-otsakkeen.dispositionmäärittääContent-Disposition-otsakkeen, jolloin selain lataa tiedoston annetulla nimellä sen sijaan, että näyttäisi sen selaimessa.
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"',
});
}
}Otsakkeet: @Header vai StreamableFile-asetukset
Voit määrittää otsakkeet myös @Header()-koristelijalla, mutta molempien käyttäminen voi aiheuttaa ristiriitoja. Suosi StreamableFile-asetuksia, koska Nest soveltaa niitä yhdenmukaisesti molemmilla adaptereilla.
- Käytä
@Header('Content-Type', ...)-määritystä vain, kun arvo on staattinen ja tiedossa käännösaikana. - Käytä
StreamableFile-oliontype/disposition-asetuksia, kun arvo lasketaan pyyntökohtaisesti (esimerkiksi dynaaminen tiedostonimi).
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'));
}
}Generoidun sisällön suoratoisto (tiedostoa ei ole levyllä)
StreamableFile ei rajoitu levyllä oleviin tiedostoihin. Mikä tahansa Readable toimii — myös lennossa tuotettu data. Tämä sopii erinomaisesti suurten CSV-vientien luomiseen rivi kerrallaan tietokannan kursorista.
Alla generaattori tuottaa CSV-rivejä laiskasti, joten koko aineistoa ei koskaan muodosteta kerralla muistiin.
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()));
}
}Backpressure: miksi suoratoisto säästää muistia
Backpressure on mekanismi, joka pitää suoratoiston turvallisena. Kun asiakas (tai verkko) on hidas, kirjoitettava puoli ilmoittaa luettavalle puolelle, että palojen tuottaminen on keskeytettävä.
- Nopea levyltä lukeminen yhdistettynä hitaaseen asiakkaaseen ei täytä muistia gigatavuilla dataa.
- Noden
pipe()(jota Nest käyttää sisäisesti) käsittelee tämän automaattisesti — se pysäyttää lähteen ja jatkaa sen suorittamista tarpeen mukaan.
Juuri siksi kannattaa palauttaa streami valtavan Bufferin sijaan.
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'));Stream-virheiden käsittely
Jos taustalla oleva streami antaa virheen sen jälkeen, kun otsakkeet on lähetetty, et voi enää lähettää JSON-muotoista virhevastausta. StreamableFile tarjoaa virheenkäsittelijän, jonka avulla voit kirjata virheen ja sulkea yhteyden hallitusti.
- Käytä
getStream().on('error', ...)-kutsua taisetErrorHandler()-menetelmää / virheenkäsittelyasetusta reagoidaksesi lukemisessa tapahtuviin virheisiin. - Ilman tätä puuttuva tiedosto voi jättää yhteyden odottamaan tai kaataa pyynnön käsittelyn.
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;
}
}Suoratoisto objektitallennuksesta (S3)
Yrityssovelluksissa tiedosto sijaitsee yleensä S3:ssa tai muussa objektitallennuksessa, ei paikallisella levyllä. S3 SDK palauttaa objektin sisällölle luettavan streamin — välitä se suoraan StreamableFile-oliolle.
- Väliaikaista tiedostoa ei tarvita, eikä koko tiedostoa ladata API-palvelimen muistiin.
- Tavut kulkevat reittiä S3 → oma API → asiakas.
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);
}
}Raakavastauksen käyttäminen @Res({ passthrough })-parametrilla
Joskus tarvitset raakavastausolion tilakoodien tai lisäotsakkeiden määrittämiseen, mutta haluat silti Nestin välittävän StreamableFile-olion streamina. Käytä @Res({ passthrough: true })-määritystä, jotta Nest hallitsee edelleen vastauksen elinkaarta.
- Ilman
passthrough: true-asetusta@Res()-olion injektointi tekee sinusta vastuullisen vastauksen päättämisestä, eikä StreamableFile-olion palauttaminen enää toimi automaattisesti.
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'));
}
}Streamin muuntaminen lennossa
Voit ketjuttaa muunnoksia ennen streamin välittämistä StreamableFile-oliolle. Yleinen käyttötapaus on suuren viennin pakkaaminen gzipillä, jolloin verkon yli siirretään vähemmän dataa — edelleen pala kerrallaan.
Tässä on itsenäinen esimerkki streamin välittämisestä muunnoksen läpi ilman palvelinta:
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'));Pikatarkistus
Testaa, ymmärrätkö StreamableFileen liittyvän keskeisen ratkaisun.
Kertaus
Tärkeimmät opit suurten vastausten suoratoistamisesta NestJS:ssä:
- Palauta
new StreamableFile(readable)sen sijaan, että puskuroisit kokonaiset tiedostot muistiin. - Voit kääriä minkä tahansa
Readable-olion:createReadStream-streamin, S3-objektin sisällön tai generaattoriin perustuvan streamin. - Määritä
typejadisposition(tai käytä@Header-määritystä), jotta asiakkaat saavat oikean MIME-tyypin ja ladattavan tiedostonimen. - Backpressure pitää muistin käytön tasaisena hitaiden asiakkaiden kanssa — tämä on koko suoratoiston tarkoitus.
- Liitä
setErrorHandler()käsittelemään lukuvirheitä, jotka tapahtuvat otsakkeiden lähettämisen jälkeen. - Käytä
@Res({ passthrough: true })-määritystä, kun tarvitset raakavastauksen käyttöön mutta haluat säilyttää Nestin suorittaman streamin välityksen.
Opi TypeScript tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 20
- Oppitunnit
- 76
Usein kysytyt kysymykset
Onko oppitunti ”Suurten vastausten suoratoisto StreamableFilella” ilmainen?
Kyllä – oppitunnin ”Suurten vastausten suoratoisto StreamableFilella” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko NestJS-yritysbackendien API:t-kurssin, päivitä CoddyKit PROhon. NestJS-yritysbackendien API:t-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Suurten vastausten suoratoisto StreamableFilella”?
Tarjoa suuret lataukset tehokkaasti StreamableFilen ja Noden luettavien virtojen avulla ilman puskurointia. Harjoittelet NestJS-yritysbackendien API:t-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni NestJS-yritysbackendien API:t-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin NestJS-yritysbackendien API:t-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.
Kuinka kauan ”Suurten vastausten suoratoisto StreamableFilella”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä NestJS-yritysbackendien API:t-oppitunnilla?
Kyllä. Jokainen NestJS-yritysbackendien API:t-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Multipart-lataukset Multer-sieppaajilla
- Suurten vastausten suoratoisto StreamableFilella
- Suorat S3-lataukset allekirjoitetuilla URL-osoitteilla
- Kuvankäsittelyputket Sharpilla