1- /* This is an example of sync copying of large files.
2- * NEVER DO THIS; ONLY FOR TESTING PURPOSES. WILL CAUSE
3- * SEVERE BACKPRESSURE AND HORRIBLE PERFORMANCE.
4- * Try navigating to the adderss with Chrome and see the video
5- * in real time. */
6-
71const uWS = require ( 'uWebSockets.js' ) ;
82const fs = require ( 'fs' ) ;
93
104const port = 9001 ;
11- const fileName = 'spritefright.mp4' ;
12- const videoFile = toArrayBuffer ( fs . readFileSync ( fileName ) ) ;
13- const totalSize = videoFile . byteLength ;
5+ const fileName = 'Sintel.2010.1080p.mkv' ;
6+
7+ /**
8+ * Optimized to return a Uint8Array view of the Node.js Buffer
9+ * without creating a new ArrayBuffer copy.
10+ */
11+ function getFileView ( name ) {
12+ const buffer = fs . readFileSync ( name ) ;
13+ // This creates a Uint8Array pointing to the EXACT same memory Node.js allocated
14+ return new Uint8Array ( buffer . buffer , buffer . byteOffset , buffer . byteLength ) ;
15+ }
16+
17+ const videoView = getFileView ( fileName ) ;
18+ const totalSize = videoView . byteLength ;
1419
1520console . log ( 'WARNING: NEVER DO LIKE THIS; WILL CAUSE HORRIBLE BACKPRESSURE!' ) ;
1621console . log ( 'Video size is: ' + totalSize + ' bytes' ) ;
1722
18- const CHUNK_SIZE = 1024 * 64 ;
23+ const CHUNK_SIZE = 1024 * 256 ;
1924
20- /* Helper function converting Node.js buffer to ArrayBuffer */
21- function toArrayBuffer ( buffer ) {
22- return buffer . buffer . slice ( buffer . byteOffset , buffer . byteOffset + buffer . byteLength ) ;
23- }
24-
25- /* Yes, you can easily swap to SSL streaming by uncommenting here */
2625const app = uWS . App ( {
27- key_file_name : 'misc/key.pem' ,
28- cert_file_name : 'misc/cert.pem' ,
29- passphrase : '1234'
30-
31- } ) . get ( '/spritefright.mp4' , ( res , req ) => {
26+ key_file_name : 'misc/key.pem' ,
27+ cert_file_name : 'misc/cert.pem' ,
28+ passphrase : '1234'
29+ } ) . get ( '/spritefright.mkv' , ( res , req ) => {
3230 let aborted = false ;
3331 let written = 0 ;
3432
@@ -39,42 +37,44 @@ const app = uWS.App({
3937 const stream = ( ) => {
4038 let ok = true ;
4139
42- // Use res.cork to pack multiple small writes into one syscall
4340 res . cork ( ( ) => {
44- while ( ok && written < videoFile . byteLength ) {
45- let chunk = videoFile . slice ( written , Math . min ( written + CHUNK_SIZE , videoFile . byteLength ) ) ;
41+ while ( ok && written < totalSize ) {
42+ const end = Math . min ( written + CHUNK_SIZE , totalSize ) ;
4643
47- // If this is the last chunk, use end() instead of write()
48- if ( written + chunk . byteLength === videoFile . byteLength ) {
49- res . end ( chunk ) ;
50- written += chunk . byteLength ;
51- ok = false ; // Stop the loop
44+ /**
45+ * .subarray() is a PURE VIEW.
46+ * No new memory is allocated for the file data here.
47+ */
48+ const chunk = videoView . subarray ( written , end ) ;
49+
50+ if ( end === totalSize ) {
51+ res . write ( chunk ) ;
52+ res . end ( ) ;
53+ written = totalSize ;
54+ ok = false ;
5255 } else {
5356 ok = res . write ( chunk ) ;
54- written += chunk . byteLength ;
57+ written = end ;
5558 }
5659 }
5760 } ) ;
5861
5962 return ok ;
6063 } ;
6164
62- // Initial attempt to stream
6365 let ok = stream ( ) ;
6466
65- // If we hit backpressure, set up the drain handler
66- if ( ! ok && ! aborted && written < videoFile . byteLength ) {
67+ if ( ! ok && ! aborted && written < totalSize ) {
6768 res . onWritable ( ( offset ) => {
6869 return stream ( ) ;
6970 } ) ;
7071 }
7172} ) . get ( '/*' , ( res , req ) => {
72- /* Make sure to always handle every route */
73- res . end ( 'Nothing to see here!' ) ;
73+ res . end ( 'Nothing to see here!' ) ;
7474} ) . listen ( port , ( token ) => {
75- if ( token ) {
76- console . log ( 'Listening to port ' + port ) ;
77- } else {
78- console . log ( 'Failed to listen to port ' + port ) ;
79- }
80- } ) ;
75+ if ( token ) {
76+ console . log ( 'Listening to port ' + port ) ;
77+ } else {
78+ console . log ( 'Failed to listen to port ' + port ) ;
79+ }
80+ } ) ;
0 commit comments