12

I'm using sharp to resize an uploaded image in a Node.js / Express application written in Typescript. After successful resizing, I'd like to delete the original file. For png and gif input images, the operation terminates successfully, I have the resized image and the original is deleted. For jpg and tif images, the resize is successful, however the unlink command fails with the following error:

EBUSY: resource busy or locked, unlink '...'

as if the sharp().resize() would still keep the input file locked, even after the completion of the resize operation.

Here is the code to test the described behavior:

import { existsSync, unlinkSync } from "fs";
import { normalize, parse } from "path";

var argv = require("yargs").argv;
var sharp = require("sharp");
var appRoot = require("app-root-path") + "/";

let resizeTest = async function (filename: string): Promise<boolean> {
    try {
        let nameParts = parse(filename);
        let source = appRoot + filename;
        let destination = appRoot + nameParts.name + "_resized" + nameParts.ext;
        let fileExists = await existsSync(source);
        if (!fileExists) {
            console.log("Input file not found. Exiting.");
            return false;
        }

        let resizeResult = await sharp(source)
            .resize(128, 128)
            .toFile(destination);
        console.log("Resize operation terminated: ", resizeResult);

        await unlinkSync(source);
        console.log("unlinkSync operation terminated.");

        return true;
    } catch (error) {
        console.log("An error occured during resizeTest execution: ", error.message);
        return false;
    }
}

if (argv._.length === 0) {
    console.log("Usage: node sharptest.js FILENAME");
} else {
    let resizeResult: Promise<boolean> = resizeTest(argv._[0]);
    resizeResult.then(result => console.log("Returning from execution with ", result));
}

What am I missing?

András Szepesházi
  • 5,774
  • 3
  • 37
  • 55
  • Does `sharp` work with promises? Can it be `await`ed on? – robertklep Dec 22 '16 at 18:18
  • @robertklep yes according to the docs: http://sharp.dimens.io/en/stable/api-output/ The toFile() returns a promise when no callback is provided. – András Szepesházi Dec 22 '16 at 18:23
  • Generally, calling `unlink` on a file that's still open isn't a problem on Unix-type OS'es, which I assumed you were using because of you're use of forward slashes. If you're on Windows, check out [this issue](https://github.com/lovell/sharp/issues/487), and its solution. – robertklep Dec 22 '16 at 18:29
  • Perfect! That was it. Setting cache temporarily to false solved the issue. Add this please as an answer so I can upvote + accept it. – András Szepesházi Dec 22 '16 at 18:47
  • What version of sharp are you using? I'm using 0.17.1 with Typescript and I got the following error message Could not find a declaration file for module 'sharp'. '/code/node_modules/sharp/lib/index.js' implicitly has an 'any' type. (7016) – hpelleti Feb 07 '17 at 23:31

2 Answers2

13

I initially was thrown by your use of forward slashes, assuming that you'd be using a Unix-type OS, where calling unlink of a file that is still open generally isn't a problem.

However, on Windows, I think that open files are usually protected from being deleted, and this issue describes a similar problem, and also a solution: internally, sharp maintains a cache of (open) files, which will block the original file from being deleted.

If you disable that cache, the problem should be fixed:

// add this at the top of your code
sharp.cache({ files : 0 });

Documented here.

EDIT: as noted in some of the comments, the code above may not fix the problem. Instead, use this:

sharp.cache(false);
robertklep
  • 174,329
  • 29
  • 336
  • 330
  • 1
    A bit of correction: sharp(...).cache is not a function. I had to call sharp.cache(...) and then separately the constructor sharp(...) with the chained commands. – András Szepesházi Dec 22 '16 at 19:10
  • 1
    @AndrásSzepesházi thanks for the clarification, the entire `cache` function seems to be missing from the documentation so I had to guess how it would work. I'll update my answer :D – robertklep Dec 22 '16 at 19:11
  • It's not trivial to find, cache is under utilities: http://sharp.dimens.io/en/stable/api-utility/#cache – András Szepesházi Dec 22 '16 at 19:12
  • 4
    Just a small note: on windows, `sharp.cache({ files : 0 });` didn't work for me, but `sharp.cache(false);` did. – Cosmin Ababei Dec 28 '16 at 20:02
  • 1
    @Cosmin Ababi Same here. `sharp.cache({ files : 0 });` didn't work for me. – Jeff Jul 11 '18 at 10:13
0

internally, sharp maintains a cache of (open) files, which will block the original file from being deleted.

sharp.cache(false);

above line add starting of your REST API