QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsziputils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsziputils.cpp
3  ---------------------
4  begin : Jul 2017
5  copyright : (C) 2017 by Paul Blottiere
6  email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <fstream>
17 
18 #include <QFileInfo>
19 #include <QDir>
20 
21 #include "zip.h"
22 
23 #include "qgsmessagelog.h"
24 #include "qgsziputils.h"
25 #include "qgslogger.h"
26 
27 #include <iostream>
28 
29 bool QgsZipUtils::isZipFile( const QString &filename )
30 {
31  return QFileInfo( filename ).suffix().compare( QLatin1String( "qgz" ), Qt::CaseInsensitive ) == 0;
32 }
33 
34 bool QgsZipUtils::unzip( const QString &zipFilename, const QString &dir, QStringList &files )
35 {
36  files.clear();
37 
38  if ( !QFileInfo::exists( zipFilename ) )
39  {
40  QgsMessageLog::logMessage( QObject::tr( "Error zip file does not exist: '%1'" ).arg( zipFilename ) );
41  return false;
42  }
43  else if ( zipFilename.isEmpty() )
44  {
45  QgsMessageLog::logMessage( QObject::tr( "Error zip filename is empty" ) );
46  return false;
47  }
48  else if ( !QDir( dir ).exists( dir ) )
49  {
50  QgsMessageLog::logMessage( QObject::tr( "Error output dir does not exist: '%1'" ).arg( dir ) );
51  return false;
52  }
53  else if ( !QFileInfo( dir ).isDir() )
54  {
55  QgsMessageLog::logMessage( QObject::tr( "Error output dir is not a directory: '%1'" ).arg( dir ) );
56  return false;
57  }
58  else if ( !QFileInfo( dir ).isWritable() )
59  {
60  QgsMessageLog::logMessage( QObject::tr( "Error output dir is not writable: '%1'" ).arg( dir ) );
61  return false;
62  }
63 
64  int rc = 0;
65  const QByteArray fileNamePtr = zipFilename.toUtf8();
66  struct zip *z = zip_open( fileNamePtr.constData(), ZIP_CHECKCONS, &rc );
67 
68  if ( rc == ZIP_ER_OK && z )
69  {
70  const int count = zip_get_num_files( z );
71  if ( count != -1 )
72  {
73  struct zip_stat stat;
74 
75  for ( int i = 0; i < count; i++ )
76  {
77  zip_stat_index( z, i, 0, &stat );
78  const size_t len = stat.size;
79 
80  struct zip_file *file = zip_fopen_index( z, i, 0 );
81  const std::unique_ptr< char[] > buf( new char[len] );
82  if ( zip_fread( file, buf.get(), len ) != -1 )
83  {
84  const QString fileName( stat.name );
85  const QFileInfo newFile( QDir( dir ), fileName );
86 
87  // Create path for a new file if it does not exist.
88  if ( !newFile.absoluteDir().exists() )
89  {
90  if ( !QDir( dir ).mkpath( newFile.absolutePath() ) )
91  QgsMessageLog::logMessage( QObject::tr( "Failed to create a subdirectory %1/%2" ).arg( dir ).arg( fileName ) );
92  }
93 
94  QFile outFile( newFile.absoluteFilePath() );
95  if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
96  {
97  QgsMessageLog::logMessage( QObject::tr( "Could not write to %1" ).arg( newFile.absoluteFilePath() ) );
98  }
99  else
100  {
101  outFile.write( buf.get(), len );
102  }
103  zip_fclose( file );
104  files.append( newFile.absoluteFilePath() );
105  }
106  else
107  {
108  zip_fclose( file );
109  QgsMessageLog::logMessage( QObject::tr( "Error reading file: '%1'" ).arg( zip_strerror( z ) ) );
110  return false;
111  }
112  }
113  }
114  else
115  {
116  zip_close( z );
117  QgsMessageLog::logMessage( QObject::tr( "Error getting files: '%1'" ).arg( zip_strerror( z ) ) );
118  return false;
119  }
120 
121  zip_close( z );
122  }
123  else
124  {
125  QgsMessageLog::logMessage( QObject::tr( "Error opening zip archive: '%1' (Error code: %2)" ).arg( z ? zip_strerror( z ) : zipFilename ).arg( rc ) );
126  return false;
127  }
128 
129  return true;
130 }
131 
132 bool QgsZipUtils::zip( const QString &zipFilename, const QStringList &files )
133 {
134  if ( zipFilename.isEmpty() )
135  {
136  QgsMessageLog::logMessage( QObject::tr( "Error zip filename is empty" ) );
137  return false;
138  }
139 
140  int rc = 0;
141  const QByteArray zipFileNamePtr = zipFilename.toUtf8();
142  struct zip *z = zip_open( zipFileNamePtr.constData(), ZIP_CREATE, &rc );
143 
144  if ( rc == ZIP_ER_OK && z )
145  {
146  for ( const auto &file : files )
147  {
148  const QFileInfo fileInfo( file );
149  if ( !fileInfo.exists() )
150  {
151  QgsMessageLog::logMessage( QObject::tr( "Error input file does not exist: '%1'" ).arg( file ) );
152  zip_close( z );
153  return false;
154  }
155 
156  const QByteArray fileNamePtr = file.toUtf8();
157  zip_source *src = zip_source_file( z, fileNamePtr.constData(), 0, 0 );
158  if ( src )
159  {
160  const QByteArray fileInfoPtr = fileInfo.fileName().toUtf8();
161 #if LIBZIP_VERSION_MAJOR < 1
162  rc = ( int ) zip_add( z, fileInfoPtr.constData(), src );
163 #else
164  rc = ( int ) zip_file_add( z, fileInfoPtr.constData(), src, 0 );
165 #endif
166  if ( rc == -1 )
167  {
168  QgsMessageLog::logMessage( QObject::tr( "Error adding file '%1': %2" ).arg( file, zip_strerror( z ) ) );
169  zip_close( z );
170  return false;
171  }
172  }
173  else
174  {
175  QgsMessageLog::logMessage( QObject::tr( "Error creating data source '%1': %2" ).arg( file, zip_strerror( z ) ) );
176  zip_close( z );
177  return false;
178  }
179  }
180 
181  zip_close( z );
182  }
183  else
184  {
185  QgsMessageLog::logMessage( QObject::tr( "Error creating zip archive '%1': %2" ).arg( zipFilename, zip_strerror( z ) ) );
186  return false;
187  }
188 
189  return true;
190 }
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
CORE_EXPORT bool isZipFile(const QString &filename)
Returns true if the file name is a zipped file ( i.e with a '.qgz' extension, false otherwise.
Definition: qgsziputils.cpp:29
CORE_EXPORT bool unzip(const QString &zip, const QString &dir, QStringList &files)
Unzip a zip file in an output directory.
Definition: qgsziputils.cpp:34
CORE_EXPORT bool zip(const QString &zip, const QStringList &files)
Zip the list of files in the zip file.