Creating temporary files with Tempfile
The Tempfile class in Ruby provides a convenient way to create files in the host OS's temporary file location that are automatically cleaned up when they are no longer needed.
In the example below, we use Tempfile to create temporary files while downloading and generating a thumbnail for an image. If you want to run it yourself, you must have vips installed on your machine (e.g. brew install vips on macOS):
require "net/http"
def create_thumbnail(image_url)
extension = File.extname(image_url)
image_file = Tempfile.new(["image", extension], binmode: true)
image_file.write Net::HTTP.get(URI(image_url))
image_file.flush
thumbnail_file = Tempfile.new(["thumbnail", ".jpg"], binmode: true)
system("vipsthumbnail #{image_file.path} -o #{thumbnail_file.path}")
thumbnail_file.read
end
url = "https://lovingruby.com/images/about.the.poignant.guide-3.gif"
jpg_data = create_thumbnail(url)
puts "Thumbnail data: #{jpg_data.byteslice(0, 10).inspect}..."
# => Thumbnail data: "\xFF\xD8\xFF\xE1\x00\xBCExif"...
const { mkdtemp, writeFile, readFile, rm } = require("node:fs/promises");
const { tmpdir } = require("node:os");
const path = require("node:path");
const { execFile } = require("node:child_process");
const { promisify } = require("node:util");
const execFileAsync = promisify(execFile);
async function createThumbnail(imageUrl) {
const tempDir = await mkdtemp(path.join(tmpdir(), "thumb-"));
try {
const extension = path.extname(new URL(imageUrl).pathname);
const imagePath = path.join(tempDir, `image${extension}`);
const thumbnailPath = path.join(tempDir, "thumbnail.jpg");
const response = await fetch(imageUrl);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
await writeFile(
imagePath, Buffer.from(await response.arrayBuffer())
);
await execFileAsync(
"vipsthumbnail", [imagePath, "-o", thumbnailPath]
);
return await readFile(thumbnailPath);
} finally {
await rm(tempDir, { recursive: true, force: true });
}
}
(async () => {
const url = "https://lovingruby.com/images/about.the.poignant.guide-3.gif";
const jpgData = await createThumbnail(url);
console.log(`Thumbnail data: ${jpgData.subarray(0, 10)}...`);
// => Thumbnail data: �����Exif...
})();
Note how we don't have to manually delete the temporary files as they will be deleted automatically when the Tempfile objects are garbage collected.
The filenames of Tempfile instances are random but will include the provided prefix and suffix, allowing us to recognize the files on disk while not having to worry about filename collisions.
History
Tempfile was added early on in Ruby's history, at some point between 1.0 and 1.2.
The underlying inspiration was likely the tmpfile function in the C standard library, which creates a temporary file that is automatically deleted when closed.
In other programming languages, Perl had File::Temp->new, which similarly provides a temporary file that automatically gets deleted on object destruction.
Not much has happened with Tempfile since its introduction, though Ruby 2.1 added the Tempfile.create method which returns an ordinary File rather than a Tempfile. Depending on how it is invoked, the temporary file may not be automatically deleted, so pay attention to the documentation if you choose to use that method.