php - unlink throws error: Resource temporarily un

2020-08-24 04:26发布

问题:

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.

回答1:

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.



回答2:

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.

:)



回答3:

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.



回答4:

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?



回答5:

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.