Work with Streams vs File Paths

Overview

GroupDocs.Assembly for Python via .NET supports both file paths and streams for template loading and document saving. Understanding when to use each approach helps you build efficient, scalable applications. File paths are simpler for local file system operations, while streams are essential for memory-based processing, remote templates, and web applications.

The main class involved is:

Here are the considerations for choosing between streams and file paths:

  • Use file paths for simple local file system operations
  • Use streams for memory-based processing, remote templates, or web applications
  • Open a file as a binary stream when one side is a file and the other is in memory
  • Always close the streams you open, preferably with a with block
Note
assemble_document takes the template and the report as two paths or as two streams, optionally with LoadSaveOptions before the data sources. A stream is a binary file object: open(path, "rb") or io.BytesIO. Streams can also be passed by keyword: source_stream=, target_stream= and then data_source_infos=.

When to Use File Paths

File paths are ideal for:

  • Simple local file system operations
  • Templates stored on disk
  • Output documents saved to disk
  • Scripts and desktop applications with direct file access
from decimal import Decimal

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# Simple file path approach
template_path = "delivery-invoice.docx"
output_path = "delivery-invoice-report.docx"

data = {"CustomerName": "Quayside Bistro", "Amount": Decimal("552.00")}

assembler = DocumentAssembler()
assembler.assemble_document(template_path, output_path, DataSourceInfo(data, "invoice"))

print("Document assembled using file paths.")

The following sample file is used in this example: delivery-invoice.docx

Binary file (DOCX, 7 KB)

Download full output

When to Use Streams

Streams are essential for:

  • Memory-based processing (no disk I/O)
  • Templates loaded from databases or remote sources
  • Web applications serving documents directly
  • Processing templates in memory for performance
import io
from datetime import datetime

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# Load template into memory
with open("stock-report.docx", "rb") as f:
    template_bytes = f.read()

with io.BytesIO(template_bytes) as template_stream, io.BytesIO() as output_stream:
    data = {"Title": "Quarterly stock report", "Date": datetime(2015, 3, 31)}

    assembler = DocumentAssembler()
    assembler.assemble_document(template_stream, output_stream, DataSourceInfo(data, "report"))

    # Use output stream (e.g., send via HTTP, save to database)
    output_bytes = output_stream.getvalue()

with open("stock-report-report.docx", "wb") as f:
    f.write(output_bytes)

print("Document assembled using streams.")

The following sample file is used in this example: stock-report.docx

Binary file (DOCX, 7 KB)

Download full output

Mixed Approach: File Input, Stream Output

assemble_document has no form that takes a path for one side and a stream for the other. To load the template from a file and keep the report in memory, open the file as a stream:

import io

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# Template from a file, output to a memory stream
with open("stock-item.docx", "rb") as template_stream, io.BytesIO() as output_stream:
    data = {"Name": "Smoked sea salt", "Value": 100}

    assembler = DocumentAssembler()
    assembler.assemble_document(template_stream, output_stream, DataSourceInfo(data, "item"))

    # Use output stream (e.g., send via HTTP response)
    output_bytes = output_stream.getvalue()

print("Document assembled, output size: {0} bytes".format(len(output_bytes)))

The following sample file is used in this example: stock-item.docx

Document assembled, output size: 7529 bytes

Download full output

Mixed Approach: Stream Input, File Output

To load the template from memory and save the report to a file, pass the file, opened for writing in binary mode, as the output stream:

import io
from datetime import datetime

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# Template from a memory stream
with open("stock-report.docx", "rb") as f:
    template_bytes = f.read()

data = {"Title": "Monthly stock report", "Date": datetime(2015, 4, 30)}

# Report to a file opened for writing
with io.BytesIO(template_bytes) as template_stream, open("stock-report-from-stream.docx", "wb") as output_stream:
    assembler = DocumentAssembler()
    assembler.assemble_document(template_stream, output_stream, DataSourceInfo(data, "report"))

print("Document assembled from stream to file.")

The following sample file is used in this example: stock-report.docx

Binary file (DOCX, 7 KB)

Download full output

Web Application Example

In web applications, streams are typically preferred: the template comes as bytes and the response body is bytes. This function fits a Flask, Django or FastAPI view:

import io

from groupdocs.assembly import DataSourceInfo, DocumentAssembler


def generate_document_for_web(template_bytes, data):
    with io.BytesIO(template_bytes) as template_stream, io.BytesIO() as output_stream:
        assembler = DocumentAssembler()
        assembler.assemble_document(template_stream, output_stream, DataSourceInfo(data, "data"))
        return output_stream.getvalue()


with open("web-order.docx", "rb") as f:
    template_bytes = f.read()

order = {"CustomerName": "Pier Nine Kitchen", "ProductName": "Smoked sea salt", "Quantity": 50}
document_bytes = generate_document_for_web(template_bytes, order)

with open("web-order-report.docx", "wb") as f:
    f.write(document_bytes)

The following sample file is used in this example: web-order.docx

Binary file (DOCX, 7 KB)

Download full output

Loading Template from Remote Source

Streams enable loading templates from URLs or remote sources. To keep the example runnable, it opens the template’s own file: URL; an https:// address works the same way:

import io
import pathlib
from urllib.request import urlopen

from groupdocs.assembly import DataSourceInfo, DocumentAssembler


def load_template_from_url(url, data):
    # Download template
    response = urlopen(url)
    try:
        template_bytes = response.read()
    finally:
        response.close()

    with io.BytesIO(template_bytes) as template_stream, io.BytesIO() as output_stream:
        assembler = DocumentAssembler()
        assembler.assemble_document(template_stream, output_stream, DataSourceInfo(data, "data"))
        return output_stream.getvalue()


template_url = pathlib.Path("web-order.docx").resolve().as_uri()
order = {"CustomerName": "Saltmarsh Deli", "ProductName": "Smoked gouda", "Quantity": 30}

with open("web-order-from-url.docx", "wb") as f:
    f.write(load_template_from_url(template_url, order))

The following sample file is used in this example: web-order.docx

Binary file (DOCX, 7 KB)

Download full output

Stream Disposal Best Practices

Always close the streams you open. A with block closes them even when the assembly raises:

import io

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# Good: the with block closes both streams
with open("stock-item.docx", "rb") as template_stream, io.BytesIO() as output_stream:
    data = {"Name": "Chestnut honey", "Value": 190}

    assembler = DocumentAssembler()
    assembler.assemble_document(template_stream, output_stream, DataSourceInfo(data, "item"))

    with open("stock-item-report.docx", "wb") as f:
        f.write(output_stream.getvalue())
# Streams are closed here

print("Streams properly disposed.")

The following sample file is used in this example: stock-item.docx

Binary file (DOCX, 7 KB)

Download full output

Warning
DocumentAssembler closes neither the template stream nor the output stream: both are still open when assemble_document returns, and the output io.BytesIO is positioned at 0 with the whole document in getvalue(). Closing them is up to your code.

Performance Considerations

  • File Paths: Simpler; the engine reads and writes the files itself
  • Streams: More flexible; nothing touches the disk unless you write it
  • Memory Streams (io.BytesIO): Best for small to medium documents, since the whole document is held in memory
  • File Streams (open(path, "rb")): Read a template from disk without loading it into a bytes object first

Advanced Usage Topics

To learn more about advanced stream handling and performance optimization, please refer to the advanced usage section.

More resources

GitHub Examples

You may easily run the code above and see the feature in action in our GitHub examples:

Free Online Apps

Along with the full-featured library, we provide simple but powerful free online apps.

To assemble documents from templates and data sources, you can use the online apps from the GroupDocs.Assembly App Product Family.

Close
Loading

Analyzing your prompt, please hold on...

An error occurred while retrieving the results. Please refresh the page and try again.