mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-30 00:29:39 -05:00
Add helper methods to get filename from content disposition headers
This commit is contained in:
parent
4c16521431
commit
75355386c2
@ -60,6 +60,13 @@ Optionally, authentication configuration can be set via the ``authcfg`` argument
|
||||
Returns a reference to the network reply
|
||||
|
||||
:return: QNetworkReply for fetched URL content
|
||||
%End
|
||||
|
||||
QString contentDispositionFilename() const;
|
||||
%Docstring
|
||||
Returns the associated filename from the reply's content disposition header, if present.
|
||||
|
||||
.. versionadded:: 3.28
|
||||
%End
|
||||
|
||||
QString contentAsString() const;
|
||||
|
||||
@ -75,6 +75,13 @@ May return ``None`` if the request has not yet completed.
|
||||
the :py:func:`QgsNetworkContentFetcherTask.fetched()` signal.
|
||||
%End
|
||||
|
||||
QString contentDispositionFilename() const;
|
||||
%Docstring
|
||||
Returns the associated filename from the reply's content disposition header, if present.
|
||||
|
||||
.. versionadded:: 3.28
|
||||
%End
|
||||
|
||||
QString contentAsString() const;
|
||||
%Docstring
|
||||
Returns the fetched content as a string
|
||||
|
||||
@ -125,6 +125,20 @@ can only be done once.
|
||||
Blocking network requests (see :py:class:`QgsBlockingNetworkRequest`) will automatically populate this content.
|
||||
|
||||
.. seealso:: :py:func:`setContent`
|
||||
%End
|
||||
|
||||
static QString extractFilenameFromContentDispositionHeader( QNetworkReply *reply );
|
||||
%Docstring
|
||||
Extracts the filename component of the content disposition header from a network ``reply``.
|
||||
|
||||
.. versionadded:: 3.28
|
||||
%End
|
||||
|
||||
static QString extractFileNameFromContentDispositionHeader( const QString &header );
|
||||
%Docstring
|
||||
Extracts the filename component of the content disposition header from the ``header``.
|
||||
|
||||
.. versionadded:: 3.28
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
@ -96,6 +96,11 @@ QNetworkReply *QgsNetworkContentFetcher::reply()
|
||||
return mReply;
|
||||
}
|
||||
|
||||
QString QgsNetworkContentFetcher::contentDispositionFilename() const
|
||||
{
|
||||
return mReply ? QgsNetworkReplyContent::extractFilenameFromContentDispositionHeader( mReply ) : QString();
|
||||
}
|
||||
|
||||
QString QgsNetworkContentFetcher::contentAsString() const
|
||||
{
|
||||
if ( !mContentLoaded || !mReply )
|
||||
|
||||
@ -73,6 +73,13 @@ class CORE_EXPORT QgsNetworkContentFetcher : public QObject
|
||||
*/
|
||||
QNetworkReply *reply();
|
||||
|
||||
/**
|
||||
* Returns the associated filename from the reply's content disposition header, if present.
|
||||
*
|
||||
* \since QGIS 3.28
|
||||
*/
|
||||
QString contentDispositionFilename() const;
|
||||
|
||||
/**
|
||||
* Returns the fetched content as a string
|
||||
* \returns string containing network content
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
#include "qgsnetworkcontentfetchertask.h"
|
||||
#include "qgsnetworkcontentfetcher.h"
|
||||
#include "qgsnetworkreply.h"
|
||||
#include <QEventLoop>
|
||||
|
||||
QgsNetworkContentFetcherTask::QgsNetworkContentFetcherTask( const QUrl &url, const QString &authcfg, QgsTask::Flags flags, const QString &description )
|
||||
@ -90,6 +91,11 @@ QNetworkReply *QgsNetworkContentFetcherTask::reply()
|
||||
return mFetcher ? mFetcher->reply() : nullptr;
|
||||
}
|
||||
|
||||
QString QgsNetworkContentFetcherTask::contentDispositionFilename() const
|
||||
{
|
||||
return mFetcher ? mFetcher->contentDispositionFilename() : QString();
|
||||
}
|
||||
|
||||
QString QgsNetworkContentFetcherTask::contentAsString() const
|
||||
{
|
||||
return mFetcher ? mFetcher->contentAsString() : QString();
|
||||
|
||||
@ -87,6 +87,13 @@ class CORE_EXPORT QgsNetworkContentFetcherTask : public QgsTask
|
||||
*/
|
||||
QNetworkReply *reply();
|
||||
|
||||
/**
|
||||
* Returns the associated filename from the reply's content disposition header, if present.
|
||||
*
|
||||
* \since QGIS 3.28
|
||||
*/
|
||||
QString contentDispositionFilename() const;
|
||||
|
||||
/**
|
||||
* Returns the fetched content as a string
|
||||
*
|
||||
|
||||
@ -15,6 +15,8 @@
|
||||
|
||||
#include "qgsnetworkreply.h"
|
||||
#include <QNetworkReply>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
|
||||
QgsNetworkReplyContent::QgsNetworkReplyContent( QNetworkReply *reply )
|
||||
: mError( reply->error() )
|
||||
@ -75,3 +77,38 @@ QByteArray QgsNetworkReplyContent::rawHeader( const QByteArray &headerName ) con
|
||||
}
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QString QgsNetworkReplyContent::extractFilenameFromContentDispositionHeader( QNetworkReply *reply )
|
||||
{
|
||||
if ( !reply )
|
||||
return QString();
|
||||
|
||||
return extractFileNameFromContentDispositionHeader( reply->header( QNetworkRequest::ContentDispositionHeader ).toString() );
|
||||
}
|
||||
|
||||
QString QgsNetworkReplyContent::extractFileNameFromContentDispositionHeader( const QString &header )
|
||||
{
|
||||
const thread_local QRegularExpression rx( QStringLiteral( R"""(filename[^;\n]*=\s*(UTF-\d['"]*)?((['"]).*?[.]$\2|[^;\n]*)?)""" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
|
||||
|
||||
QRegularExpressionMatchIterator i = rx.globalMatch( header, 0 );
|
||||
QString res;
|
||||
// we want the last match here, as that will have the UTF filename when present
|
||||
while ( i.hasNext() )
|
||||
{
|
||||
const QRegularExpressionMatch match = i.next();
|
||||
res = match.captured( 2 );
|
||||
}
|
||||
|
||||
if ( res.startsWith( '"' ) )
|
||||
{
|
||||
res = res.mid( 1 );
|
||||
if ( res.endsWith( '"' ) )
|
||||
res.chop( 1 );
|
||||
}
|
||||
if ( !res.isEmpty() )
|
||||
{
|
||||
res = QUrl::fromPercentEncoding( res.toUtf8() );
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -157,6 +157,20 @@ class CORE_EXPORT QgsNetworkReplyContent
|
||||
*/
|
||||
QByteArray content() const { return mContent; }
|
||||
|
||||
/**
|
||||
* Extracts the filename component of the content disposition header from a network \a reply.
|
||||
*
|
||||
* \since QGIS 3.28
|
||||
*/
|
||||
static QString extractFilenameFromContentDispositionHeader( QNetworkReply *reply );
|
||||
|
||||
/**
|
||||
* Extracts the filename component of the content disposition header from the \a header.
|
||||
*
|
||||
* \since QGIS 3.28
|
||||
*/
|
||||
static QString extractFileNameFromContentDispositionHeader( const QString &header );
|
||||
|
||||
private:
|
||||
|
||||
QNetworkReply::NetworkError mError = QNetworkReply::NoError;
|
||||
|
||||
@ -220,6 +220,7 @@ ADD_PYTHON_TEST(PyQgsNetworkAccessManager test_qgsnetworkaccessmanager.py)
|
||||
ADD_PYTHON_TEST(PyQgsNetworkContentFetcher test_qgsnetworkcontentfetcher.py)
|
||||
ADD_PYTHON_TEST(PyQgsNetworkContentFetcherRegistry test_qgsnetworkcontentfetcherregistry.py)
|
||||
ADD_PYTHON_TEST(PyQgsNetworkContentFetcherTask test_qgsnetworkcontentfetchertask.py)
|
||||
ADD_PYTHON_TEST(PyQgsNetworkReply test_qgsnetworkreply.py)
|
||||
ADD_PYTHON_TEST(PyQgsNullSymbolRenderer test_qgsnullsymbolrenderer.py)
|
||||
ADD_PYTHON_TEST(PyQgsNumericFormat test_qgsnumericformat.py)
|
||||
ADD_PYTHON_TEST(PyQgsNumericFormatGui test_qgsnumericformatgui.py)
|
||||
|
||||
45
tests/src/python/test_qgsnetworkreply.py
Normal file
45
tests/src/python/test_qgsnetworkreply.py
Normal file
@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsNetworkReplyContent
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
|
||||
from builtins import chr
|
||||
from builtins import str
|
||||
__author__ = 'Nyall Dawson'
|
||||
__date__ = '20/06/2022'
|
||||
__copyright__ = 'Copyright 2022, The QGIS Project'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
import os
|
||||
from qgis.testing import unittest, start_app
|
||||
from qgis.core import QgsNetworkReplyContent
|
||||
from utilities import unitTestDataPath
|
||||
from qgis.PyQt.QtCore import QUrl
|
||||
from qgis.PyQt.QtNetwork import QNetworkReply, QNetworkRequest
|
||||
import socketserver
|
||||
import threading
|
||||
import http.server
|
||||
|
||||
app = start_app()
|
||||
|
||||
|
||||
class TestQgsNetworkReply(unittest.TestCase):
|
||||
|
||||
def test_content_disposition_filename(self):
|
||||
self.assertEqual(QgsNetworkReplyContent.extractFileNameFromContentDispositionHeader('x'), '')
|
||||
self.assertEqual(QgsNetworkReplyContent.extractFileNameFromContentDispositionHeader('attachment; filename=content.txt'), 'content.txt')
|
||||
self.assertEqual(
|
||||
QgsNetworkReplyContent.extractFileNameFromContentDispositionHeader("attachment; filename*=UTF-8''filename.txt"), 'filename.txt')
|
||||
self.assertEqual(
|
||||
QgsNetworkReplyContent.extractFileNameFromContentDispositionHeader('attachment; filename="EURO rates"; filename*=utf-8\'\'%e2%82%ac%20rates'), '€ rates')
|
||||
self.assertEqual(
|
||||
QgsNetworkReplyContent.extractFileNameFromContentDispositionHeader('attachment; filename="omáèka.jpg"'), 'omáèka.jpg')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
x
Reference in New Issue
Block a user