8

I have hundreds of QGIS projects that have been built over years and many different QGIS versions. Now I need to move them to another server and I want to be able to edit the project files to show the new file paths.

My issue is that I cannot unzip the qgz files because my PC Administrator has not given me access to an unzipping program. Normally, if the file I want to unzip is one recognised by WinZip, then I can right click and choose Extract. But right clicking a qgz file does not give me the Extract option.

My research indicates that earlier qgs files are easy to access, but my issue is about project files created by the 3.0 series of QGIS and the most recent project file version and my IT admin's distrust of my intentions.

How else can I open the project files to access the file paths in the qgs part?

4
  • So, yes, I have access to an unzipping program - but it only recognises .zip file. I cannot use it to unzip any other zipped format.
    – Johanna
    Commented Aug 12, 2019 at 1:54
  • 2
    Try [shift] [right-click] and "open with...". Or manually - rename .qgz to .zip, extract zip, change paths, re-zip, rename .zip to .qgz. If you have python skills, you could automate this with os.walk (os.path.walk in python 2x) and zipfile.
    – user2856
    Commented Aug 12, 2019 at 2:00
  • Thank you. The [shift][right-click] doesn't do anything for me, but the renaming does.
    – Johanna
    Commented Aug 12, 2019 at 2:05
  • I've had this happen a few times lately. QGIS refuses to open a qgz file that it has created but I can unzip it using an unzipping app and the qgs file works just fine. Testing the archive reveals no errors too.
    – Damien
    Commented May 27 at 23:51

1 Answer 1

14

To fix this manually, rename .qgz to .zip, extract zip, change paths, re-zip, rename .zip to .qgz.

Alternatively, I've hacked together a very basic and very minimally tested python script to do this (Python 3 only unless you modify it):

from fnmatch import filter
from io import BytesIO
from os import (walk, path, rename)
from zipfile import ZipFile, ZIP_DEFLATED


def main(in_dir, old_path, new_path, backup_suffix=None):
    for (dirpath, dirnames, filenames) in walk(in_dir):
        qgzs = filter(filenames, '*.qgz')
        for qgz in qgzs:
            qgz_path = path.join(dirpath, qgz)
            mem_qgz = BytesIO()
            with ZipFile(qgz_path, 'r') as in_qgz, ZipFile(mem_qgz, 'w', compression=ZIP_DEFLATED) as tmp_qgz:
                for f in in_qgz.infolist():
                    data = in_qgz.read(f.filename).decode('UTF-8')
                    if f.filename.endswith('.qgs'):
                        data = data.replace('source="{}'.format(old_path), 'source="{}'.format(new_path))
                    tmp_qgz.writestr(f.filename, data)

            if backup_suffix is not None:
                backup_suffix = backup_suffix if backup_suffix.startswith('.') else '.' + backup_suffix
                try:
                    rename(qgz_path, qgz_path + backup_suffix)
                except FileExistsError as err:
                    print('Unable to backup file, skipping {} ({})'.format(qgz_path, err))
                    continue

            with open(qgz_path, 'wb') as out_qgz:
                out_qgz.write(mem_qgz.getvalue())


if __name__ == '__main__':
    backup_suffix = '.orig'  # Don't overwrite orig .qgz (just in case...), append ".orig"
    in_dir = r'D:\Temp'

    old_path = 'D:\\'  # 2 trailing backslashes on Windows, 1 forward for Unix like paths
    new_path = r'C:\\'

    main(in_dir, old_path, new_path, backup_suffix)

Note: the path changes are a kludge and rely on a simple search and replace, but the zipped .qgs file is actually XML and really should be parsed as such.

2
  • 1
    I edited your answer to include the manual method you mentioned in the comments above. Thank you, that's an excellent tip. I had no idea you could do that.
    – csk
    Commented Aug 12, 2019 at 16:26
  • A heads-up that, at least on QGIS 3.30 when save path is relative and a project home is defined in the project, source path are stored with / not \, and may have several ../.. to traverse up the tree from the project home path. So this is a great script to use, but browse a few source paths in some of your .qgs files to figure out what to use for the old and new path encoding in your situation.
    – Houska
    Commented Mar 22, 2023 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.