php - unlink throws error: Resource temporarily unavailable
Asked Answered
S

5

15

Here is the piece of code:

public function uploadPhoto(){
    $filename = '../storage/temp/image.jpg';  
    file_put_contents($filename,file_get_contents('http://example.com/image.jpg'));
    $photoService->uploadPhoto($filename);
    echo("If file exists: ".file_exists($filename));
    unlink($filename);
}

I am trying to do the following things:

  1. Get a photo from a URL and save it in a temp folder in my server. This works fine. The image file is created and echoes If file exists: 1 when echo("If file exists: ".file_exists('../storage/temp/image.jpg'));.
  2. Pass that file to another function that hanldes uploading the file to Amazon s3 bucket. The file gets stored in my s3 bucket.
  3. Delete the photo stored in the temp folder. This doesn't work! I get an error saying:

unlink(../storage/temp/image.jpg): Resource temporarily unavailable

If I use rename($filename,'../storage/temp/renimage.jpg'); instead of unlink($filename); i get an error:

rename(../storage/temp/image.jpg,../storage/temp/renimage.jpg): The process cannot access the file because it is being used by another process. (code: 32)

If I remove the function call $photoService->uploadPhoto($filename);, everything works perfectly fine.

If the file is being used by another process, how do I unlink it after the process has been completed and the file is no longer being used by any process? I do not want to use timers.

Please help! Thanks in advance.

Selle answered 29/3, 2017 at 15:25 Comment(7)
As a side comment you should use absolute file paths not relative file paths. use $_SERVER['DOCUMENT_ROOT'] in your filepath rather than ../Lutz
Dont read it fully, but use clearstatcache() before echo("If file exists: ".file_exists($filename)); php.net/manual/en/function.clearstatcache.php and maybe $photoService->uploadPhoto does the unlink before you.Feigned
Would it not be better to use an unique name instead of image.jpg,maybe diff. requests try to work on the same file.Feigned
@Lutz Not after unlink but before file_exists and who knows what uploadPhoto does. ;-) And i pasted the link, so the OP can read about it, to get it.Feigned
@JustOnUnderMillions, Yes. I have used a unique name generator in my actual code. The code snippet given above is for the purpose of posting here.Selle
All uploadPhoto does is creating an AWS client and uploading the photo to my s3 bucket. @FeignedSelle
@Martin, I have tried that too. Doesn't work.Selle
W
8

Just had to deal with a similar Error.

It seems your $photoService is holding on to the image for some reason... Since you didn't share the code of $photoService, my suggestion would be to do something like this (assuming you don't need $photoService anymore):

[...]
echo("If file exists: ".file_exists($filename));
unset($photoService);
unlink($filename);
}

The unset() method will destroy the given variable/object, so it can't "use" (or wharever it does) any files.

Weeds answered 30/6, 2017 at 10:17 Comment(1)
note that if you're running Linux or MacOS or *BSD, you don't have to close all handles prior to deleting it.. but if you're running php on MS Windows, then you need to close all handles firstCitrate
E
11

Simplest solution:

gc_collect_cycles();
unlink($file);

Does it for me! Straight after uploading a file to amazon S3 it allows me to delete the file on my server.

See here: https://github.com/aws/aws-sdk-php/issues/841

The GuzzleHttp\Stream object holds onto a resource handle until its __destruct method is called. Normally, this means that resources are freed as soon as a stream falls out of scope, but sometimes, depending on the PHP version and whether a script has yet filled the garbage collector's buffer, garbage collection can be deferred. gc_collect_cycles will force the collector to run and call __destruct on all unreachable stream objects.

:)

Enchantress answered 4/4, 2019 at 15:38 Comment(0)
W
8

Just had to deal with a similar Error.

It seems your $photoService is holding on to the image for some reason... Since you didn't share the code of $photoService, my suggestion would be to do something like this (assuming you don't need $photoService anymore):

[...]
echo("If file exists: ".file_exists($filename));
unset($photoService);
unlink($filename);
}

The unset() method will destroy the given variable/object, so it can't "use" (or wharever it does) any files.

Weeds answered 30/6, 2017 at 10:17 Comment(1)
note that if you're running Linux or MacOS or *BSD, you don't have to close all handles prior to deleting it.. but if you're running php on MS Windows, then you need to close all handles firstCitrate
P
4

I sat over this problem for an hour or two, and finally realized that "temporarily unavailable" really means "temporarily".

In my case, concurrent PHP scripts access the file, either writing or reading. And when the unlink() process had a poor timing, then the whole thing failed.

The solution was quite simple: Use the (generally not very advisable) @ to prevent the error being shown to the user (sure, one could also stop errors from beinf printed), and then have another try:

$gone = false;
for ($trial=0; $trial<10; $trial++) {
    if ($gone = @unlink($filename)) {
        break;
    }
    // Wait a short time
    usleep(250000);
    // Maybe a concurrent script has deleted the file in the meantime
    clearstatcache();
    if (!file_exists($filename)) {
        $gone = true;
        break;
    }
}
if (!$gone) {
    trigger_error('Warning: Could not delete file '.htmlspecialchars($filename), E_USER_WARNING);
}

After solving this issue and pushing my luck further, I could also trigger the "Resource temporarily unavailable" issue with file_put_contents(). Same solution, now everything works fine.

If I'm wise enough and/or unlinking fails in the future, I'll replace the @ by ob_start(), so the error message could tell me the exact error.

Peachey answered 30/11, 2017 at 10:13 Comment(0)
U
2

I had the same problem. The S3 Client doesn't seem to want to unlock before unlink is being executed. If you extract the contents into a variable and set it as the 'body' in the putObject array:

$fileContent = file_get_contents($filepath);
$result = $s3->putObject(array(
    'Bucket'       => $bucket,
    'Key'          => $folderPath,
    'Body'         => $fileContent,
     //'SourceFile'   => $filepath,
    'ContentType'  => 'text/csv',
    'ACL'          => 'public-read'
));

See this answer: How to unlock the file after AWS S3 Helper uploading file?

Unsnarl answered 27/9, 2017 at 14:2 Comment(0)
A
-2

The unlink method return bool value, so you can build a cycle, with some wait() and retries limit to wait for your processes to complete.

Additionally put "@" on the unlink, to hide the access error.

Throw another error/exception if retries count reached.

Autonomy answered 29/3, 2017 at 15:29 Comment(4)
these are all terrible solutions aimed at hiding the symptoms rather than solving the problem. Do not use @ error supressing and waiting will simply suck up your server processes (as PHP waits it cycles CPU power, it does not pause processing)Lutz
@Martin, I agree. Use of @ is not advisable.Selle
I audibly laughed at this. If there's an error just suppress it! haha!Starcrossed
Sure thing it's not advisable, and sure thing there are much better solutions to catch those warnings on higher php versions. Not sure why you guys have stuck to this specific part, lol. You can laugh as much as you want :) Nice to see i have done someone's day better.Autonomy

© 2022 - 2024 — McMap. All rights reserved.