Extract only extensions specific to a ZIP

Asked

Viewed 485 times

2

I am developing a system where in some part of it the client can send a file ZIP containing only images.

I’m willing to do it in a way that I can extract from this ZIP only files that contain specific image extensions.

I know that in PHP, when we list only one existing extension type in a directory, we use the function glob to do this.

Is there any way to do a similar operation with the class ZipArchive of PHP?

If anyone knows any plugin on Laravel that does something of the kind, it will be very useful such information.

Note: The Ziparchive::extractTo() method accepts a second parameter, which is an array of files from white list of the files that will be extracted.

It would be nice if that also worked with a glob brace!

  • 2

    Isn’t it easier to extract everything and enjoy only what is image? The rest you can delete throughout your routine.

  • 2

    I thought the same thing, @Rodrigorigotti. While the answer is not enough, I will use the FileSystemIterator for that reason!

2 answers

2


Use the library Extractor, that extracts the compressed files and returns an object Finder package Symfony\Component, documentation here.

See the example for your situation:

$temporaryDirectory = new Mmoreram\Extractor\Filesystem\TemporaryDirectory();
$extensionResolver = new Mmoreram\Extractor\Resolver\ExtensionResolver;
$extractor = new Mmoreram\Extractor\Extractor($temporaryDirectory, $extensionResolver);

$finder = $extractor->extractFromFile($uploadFile);

$validMimes = ["image/png", "image/jpg"];

$filter = function(\SplFileInfo $path) {
    $file = new File($path);

    if (in_array($file->getMimeType(), $validMimes)) {
        return true;
    }
};

$validFiles = $finder->files()->filter($filter);
  • I’ll take a look yes! Thank you very much

  • As soon as I’m done testing, if everything goes okay, I’ll set it right :)

  • Very good this library. To use it, you have to have the Symfony\Component\HttpFoundation\File\File installed. And when validating in $filter, must have a return false for cases of not falling into the validation of mimes. Thank you very much, friend!

0

Based on the advice given by @Rodigorigotti, I developed a code to delete undesirable extension files. The extraction is done to a secure folder, so as not to give direct access to the folder to the user (thus avoiding a PHP Injection).

 public function postAjaxUploadZip($id) {   
        $file = Input::file('zip');

        $rules = [
            'zip' => 'required|mimes:zip'
        ];

        $messages = [
            'mimes' => "Extensão de arquivo inválida"
        ];

        $validation = Validator::make(Input::all(), $rules, $messages);


        if ($validation->passes()) {

            try{

                $zip = $file->getRealPath();

                $zipObject =  new ZipArchive;
                if (! $zipObject->open($zip)) {

                    throw new RunTimeException('Não foi possível abrir o arquivo enviado');
                }

                $path = base_path("secure/{$id}");  

                if (! File::isDirectory($path)) {

                    File::makeDirectory($path, 0755);
                }

                // Extrai para uma pasta segura, para o usuário não ter acesso a esses arquivos pelo pasta "public" do Laravel
                $zipObject->extractTo($path);

                // Itera com os arquivos do diretório onde houve a extração

                $files = new FileSystemIterator($path);

                foreach ($files as $file) {

                   $data = [
                      'file' => $file->getRealPath()
                   ];

                   $rules = ['file' => 'mimes:jpg,png,bmp,jpeg'];

                   if (Validator::make($data, $rules)->fails()) {

                      $deletedFiles[] = $file->getFilename();

                   }

                }   

                File::delete($filesToDelete);


            } catch (Exception $e) {

                return Response::json([
                    'error'     => $e->getMessage(),
                    'directory' => $path,
                ]);
            }


            return Response::json([
                'error' => false, 
                'deletedFiles' => $filesToDelete
            ]);

        } else {

            return Response::json(['error' => $validation->messages()]);    
        }
    }

If anyone has a better idea, it’ll be a big help. The less code the better!

  • 1

    I don’t think it’s a good idea to check the extension by regex, as the file extension can be manipulated. Try to determine if the file really is an image by mime-type, using exif-imagetype or fileinfo

  • 1

    I can then use the Validator of Laravel for every iteration, right? It internally uses the FileInfo

  • Good idea ! : ) http://laravel.com/docs/4.2/validation#Rule-mimes

  • I updated the code with the Validation version of Laravel!

  • 1

    Very good, @gmsantos. When he was doing the tests here, he was accepting a teste.jpg that I created being that he was a txt that I had converted (by laziness) to test the sending of files in ZIP :)

  • 1

    Could be malicious code that file (Um . php disguised as image)

  • Actually, I can’t use the Validator mime because it checks if it’s an upload as well. I made another change to the source code!

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.