27

FileDialog gives a QML url variable. theurl.toString() gives something like file:///c:\foo\bar.txt. How do I get c:\foo\bar.txt?

I want to do it in a cross-platform way, and ideally without relying on regex-style hacks. QUrl provides a path() method, but I don't seem to be able to access it from QML.

Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • 2
    If you want you app will be cross-platform this URL is exactly what you need. According to `RFC 3986` URL starts with protocol. In your case it is `file://`.All Qt components understand it, even worse sometimes you will get an error while passing something like `c:/bar.txt`. If you still want to get this URL in `wrong` way you just want to replace the protocol part - `theurl.toString().replace("file:///","")` – folibis Jul 24 '14 at 09:56
  • Yeah I said I don't want to use regex style hacks. I was wondering if there was a method that actually gives me the path data directly (it is stored in the QUrl object). And this isn't to pass to a Qt component. – Timmmm Jul 24 '14 at 10:01
  • Btw, for what do you need it? – folibis Jul 24 '14 at 10:04
  • 2
    Showing the filename to the user. – Timmmm Jul 24 '14 at 13:45
  • 6
    I dug into the Qt source. The QML "url" type seems to be backed by the C++ QUrl class, which has a very nice toLocalFile() method, but that's not a Q_INVOKABLE method, sadly. – Chris Dolan Aug 06 '14 at 16:15
  • 2
    I think removing `file:///` with 3 slashes is necessary for Windows but will break on Unix. – Roman Plášil Aug 20 '17 at 03:15
  • QUrl class has `::toString(QUrl::FormattingOptions options)` method. Relevant options here is `QUrl::RemoveScheme` and `QUrl::PreferLocalFile`: http://org.qt-project.qtlocation.5151/qtcore/qurl.html#UrlFormattingOption-enum Not sure if it exposed to QML. – bam Dec 31 '20 at 15:20

3 Answers3

11

As noted in the comments already, there seems to be no way (yet?) to get the path itself without a regex. So this is the only way to go:

Basic solution

FileDialog {
    onAccepted: {
        var path = myFileDialog.fileUrl.toString();
        // remove prefixed "file:///"
        path = path.replace(/^(file:\/{3})/,"");
        // unescape html codes like '%23' for '#'
        cleanPath = decodeURIComponent(path);
        console.log(cleanPath)
    }
}

This regex should be quite robust as it only removes the file:/// from the beginning of the string.

You will also need to unescape some HTML characters (if the file name contains e.g. the hash #, this would be returned as %23. We decode this by using the JavaScript function decodeURIComponent()).

Fully featured example

If you not only want to filter the file:/// but also qrc:// and http://, you can use this RegEx:

^(file:\/{3})|(qrc:\/{2})|(http:\/{2})

So the new, complete code would be:

FileDialog {
    onAccepted: {
        var path = myFileDialog.fileUrl.toString();
        // remove prefixed "file:///"
        path= path.replace(/^(file:\/{3})|(qrc:\/{2})|(http:\/{2})/,"");
        // unescape html codes like '%23' for '#'
        cleanPath = decodeURIComponent(path);
        console.log(cleanPath)
    }
}

This is a good playground for RegEx'es: http://regex101.com/r/zC1nD5/1

mozzbozz
  • 3,052
  • 5
  • 31
  • 44
  • Ermm no, I'm using this code myself and it *works*. Of course it works for `file:///` URLs only (and not `file://` - for two __or__ three slashes, use `/^(file:\/{2,3})/`). Or what do you mean? For reference, see: http://regex101.com/r/aO1qG4/1 – mozzbozz Nov 12 '14 at 10:08
  • 5
    I mean it won't work for other schemes, like `qrc://` or `http://`. I'm not saying it won't work in many cases; just point out that it isn't the same as `QUrl::path()`. – Timmmm Nov 12 '14 at 10:56
  • I got it. I assumed, you only wanted files from the file dialog. But you are right, you could also input an `http://`-URL there. I edited my answer appropriately. I hope I did cover all cases now. But yes, of course a `QUrl::path()`-like function would be ideal. – mozzbozz Nov 12 '14 at 12:27
  • Why are you removing 3 `///` and not just 2? – pooya13 May 11 '21 at 23:18
  • @pooya13 it's been a long time ago, so take this with a grain of salt: I think the reason was that `file:///` always has 3 slashes while other "protocols" like `http://` have 2 slashes. Not even sure why. Maybe the last slash is just part of the filepath, i.e. `/home/mozzbozz` (the "root"-slash). Maybe platform dependent? Not sure (but unlikely for Qt). – mozzbozz May 19 '21 at 06:58
  • @mozzbozz Thanks for your response. Then I think you need to remove only 2 and leave the third as that seems to be the root slash. – pooya13 May 24 '21 at 06:36
10

Following Chris Dolan's answer above, it's probably neatest to deal with this using a slot in C++:

public slots:

void handleFileChosen(const QString &urlString) {
    const QUrl url(urlString);
    if (url.isLocalFile()) {
        setFile(QDir::toNativeSeparators(url.toLocalFile()));
    } else {
        setFile(urlString);
    }
}
Anthony Hayward
  • 2,164
  • 21
  • 17
4

In MS Windows "file:///c:\foo\bar.txt" should be converted to "c:\foo\bar.txt". However in Linux the url "file:///Users/data/abcdef" has the correct path as "/Users/data/abcdef". I have created a simple function to convert url to path:

function urlToPath(urlString) {
    var s
    if (urlString.startsWith("file:///")) {
        var k = urlString.charAt(9) === ':' ? 8 : 7
        s = urlString.substring(k)
    } else {
        s = urlString
    }
    return decodeURIComponent(s);
}
Tony
  • 1,551
  • 20
  • 21