Hello,
We are using a Python script to stream on the fly created archive. We need this script to allow proper auth from our main server.
This script calls /tools/create-archive
then stream the archive.
It works well, except one thing:
The archive name is always Stream-Orthanc-{id}.zip
.
I have tried with no success:
- adding
?filename=archive_name
to the url - adding
"Filename": archive_name,
to our POST json body - setting our script output Http headers:
# Setting output headers for the response does not work in this context
output.SetHttpHeader('Content-Type', 'application/zip')
output.SetHttpHeader('Content-Disposition', f'attachment; filename="{archive_name}.zip"')
Relevant part of our script is:
def create_archive(
decoded_jwt: Dict[str, Any],
orthanc_url: str,
archive_endpoint: str,
archive_name: str,
token: str,
http_client=requests
) -> requests.Response:
"""Create an archive by calling the Orthanc API."""
try:
# archive_url = f"{orthanc_url}{archive_endpoint}?filename={archive_name}"
archive_url = f"{orthanc_url}{archive_endpoint}"
logger.info(f"Creating archive at {archive_url} with filename={archive_name} & payload: {decoded_jwt}")
response = http_client.post(
archive_url,
json=decoded_jwt,
headers={'Authorization': token},
stream=True,
timeout=120
)
if response.status_code != 200:
error_msg = f"Failed to create archive, status: {response.status_code}, response: {response.text}"
logger.error(error_msg)
raise ValueError(error_msg)
return response
except requests.exceptions.RequestException as e:
error_msg = f"Archive creation request failed: {str(e)}"
logger.error(error_msg)
raise ValueError(error_msg)
def stream_archive(
archive_response: requests.Response,
output: orthanc.RestOutput,
archive_name: str,
chunk_size: int = 16 * 1024
) -> None:
"""Stream the archive to the output."""
try:
logger.info(f"archive_name: {archive_name}.zip")
output.StartStreamAnswer('application/zip')
# Setting output headers for the response does not work in this context
# output.SetHttpHeader('Content-Type', 'application/zip')
# output.SetHttpHeader('Content-Disposition', f'attachment; filename="{archive_name}.zip"')
for buffer in archive_response.iter_content(chunk_size, False):
output.SendStreamChunk(buffer)
except Exception as e:
error_msg = f"Streaming failed: {str(e)}"
logger.error(error_msg)
raise
def handle_rest_archive(output: orthanc.RestOutput, uri: str, **kwargs):
"""Main function that handles the REST request."""
try:
# Extract headers from kwargs (Orthanc passes them separately)
headers = kwargs.get('headers', {})
request_method = kwargs.get('method', 'GET')
groups = kwargs.get('groups', [])
config = kwargs.get('config', DEFAULT_CONFIG)
logger.info(f"Handling {request_method} request for {uri} from groups: {groups}")
# Get the JWT from headers
jwt_token = get_jwt_from_headers(headers)
if not jwt_token:
raise ValueError("Missing Authorization header")
# Decode JWT
decoded_jwt = decode_jwt(jwt_token, config['JwtDecodeUrl'])
logger.info(f"Decoded JWT: {decoded_jwt}")
archive_name = decoded_jwt.get('name', 'archive')
resources_ids = decoded_jwt.get('resourceList', [])
payload = {
"Resources": resources_ids,
"Asynchronous": True,
"Priority": 0,
"Synchronous": True,
# "Filename": archive_name,
}
# Create archive
archive_response = create_archive(
payload,
config['OrthancSelfUrl'],
config['ArchiveEndpoint'],
archive_name,
TOKEN
)
# Stream response
stream_archive(archive_response, output, archive_name)
except ValueError as e:
error_msg = f"Validation error: {str(e)}"
logger.error(error_msg)
output.AnswerBuffer(json.dumps({"error": error_msg}), "application/json")
except Exception as e:
error_msg = f"Unexpected error: {str(e)}"
logger.error(f"{error_msg}\n{traceback.format_exc()}")
output.AnswerBuffer(json.dumps({"error": error_msg}), "application/json")
# Registration using the correct signature
def rest_callback(output: orthanc.RestOutput, uri: str, **kwargs):
"""Wrapper that matches Orthanc's expected callback signature."""
return handle_rest_archive(output, uri, **kwargs)
orthanc.RegisterRestCallback('/stream_archive', rest_callback)
Thanks for any help