5

How can we create an empty file of certain size? I have a requirement where I need to create an empty file (i.e. file filled with zero bytes). In order to do so, this is the approach I am currently taking:

  1. I create an empty file of zero byte size.
  2. Next I keep on appending zero bytes buffer (max 2 GB at a time) to that file till I reach the desired size.

Here's the code I am using currently:

const createEmptyFileOfSize = (fileName, size) => {
  return new Promise((resolve, reject) => {
    try {
      //First create an empty file.
      fs.writeFile(fileName, Buffer.alloc(0), (error) => {
        if (error) {
          reject(error);
        } else {
          let sizeRemaining = size;
          do {
            const chunkSize = Math.min(sizeRemaining, buffer.kMaxLength);
            const dataBuffer = Buffer.alloc(chunkSize);
            try {
              fs.appendFileSync(fileName, dataBuffer);
              sizeRemaining -= chunkSize;
            } catch (error) {
              reject(error);
            }
          } while (sizeRemaining > 0);
          resolve(true);
        }
      });
    } catch (error) {
      reject(error);
    }
  });
};

While this code works and I am able to create very large files (though it takes significant time to create an empty large file [roughly about 5 seconds to create a 10 GB file]) however I am wondering if there's a better way of accomplishing this.

Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
  • Possible duplicate of [Create an empty file in Node.js?](https://stackoverflow.com/questions/12809068/create-an-empty-file-in-node-js) – Jeremy Thille Mar 22 '18 at 16:10
  • Thanks but I think my problem is a bit different. I want to create an empty file of certain size (say 1GB). – Gaurav Mantri Mar 22 '18 at 16:13

1 Answers1

9

There is no need to write anything in the file. All you have to do is to open the file for writing ('w' means "create if doesn't exist, truncate if exists") and write at least one byte to the offset you need. If the offset is larger than the current size of the file (when it exists) the file is extended to accommodate the new offset.

Your code should be as simple as this:

const fs = require('fs');

const createEmptyFileOfSize = (fileName, size) => {
    return new Promise((resolve, reject) => {
        fh = fs.openSync(fileName, 'w');
        fs.writeSync(fh, 'ok', Math.max(0, size - 2));
        fs.closeSync(fh);
        resolve(true);
    });
};

// Create a file of 1 GiB
createEmptyFileOfSize('./1.txt', 1024*1024*1024);

Please note that the code above doesn't handle the errors. It was written to show an use case. Your real production code should handle the errors (and reject the promise, of course).

Read more about fs.openSync(), fs.writeSync() and fs.closeSync().

Update

A Promise should do its processing asynchronously; the executor function passed to the constructor should end as soon as possible, leaving the Promise in the pending state. Later, when the processing completes, the Promise will use the resolve or reject callbacks passed to the executor to change its state.

The complete code, with error handling and the correct creation of a Promise could be:

const fs = require('fs');

const createEmptyFileOfSize = (fileName, size) => {
  return new Promise((resolve, reject) => {
    // Check size
    if (size < 0) {
        reject("Error: a negative size doesn't make any sense")
        return;
    }

    // Will do the processing asynchronously
    setTimeout(() => {
        try {
          // Open the file for writing; 'w' creates the file 
          // (if it doesn't exist) or truncates it (if it exists)
          fd = fs.openSync(fileName, 'w');
          if (size > 0) {
              // Write one byte (with code 0) at the desired offset
              // This forces the expanding of the file and fills the gap
              // with characters with code 0
              fs.writeSync(fd, Buffer.alloc(1), 0, 1, size - 1);
          }
          // Close the file to commit the changes to the file system
          fs.closeSync(fd);

          // Promise fulfilled
          resolve(true);
        } catch (error) {
          // Promise rejected
          reject(error);
        }
    // Create the file after the processing of the current JavaScript event loop
    }, 0)
  });
};
axiac
  • 68,258
  • 9
  • 99
  • 134
  • Thanks! Let me give it a try. – Gaurav Mantri Mar 22 '18 at 16:22
  • Worked like a charm! Thank you so much! Can you please check the code I ended up writing - https://gist.github.com/gmantri/e68ac10890df58ef46ed51eec34b45a3 and let me know if you see any issues in there. Thanks again for your help. – Gaurav Mantri Mar 23 '18 at 01:39
  • 1
    Since you create a Promise, the executor function passed to the [`Promise` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) should end as soon as possible (it is called by the `Promise` constructor). I would use `setTimeout(..., 0)` to postpone the execution of the `try/catch` block [after the completion of the current JavaScript event loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Run-to-completion). Check my comment on your gist for the code. – axiac Mar 23 '18 at 10:02
  • Updated the answer with your code + setTimeout and comments. – axiac Mar 23 '18 at 10:44