Export PostgreSQL/PostGIS Data with Attachments to File Geodatabase and Publish to ArcGIS Enterprise
Introduction
In many GIS workflows, spatial data is stored inside PostgreSQL/PostGIS while image files are stored separately on disk. A common requirement is to:
Export spatial features from PostGIS
Include image attachments
Create a File Geodatabase (.gdb)
Zip the geodatabase
Publish it to ArcGIS Enterprise as a hosted feature layer with attachments
This tutorial explains the complete workflow using ArcPy in ArcGIS Pro.
Architecture Overview
Source Database
PostgreSQL/PostGIS
Main Feature Table
wb_main
Contains:
Spatial geometry
Waterbody attributes
Unique identifier
Attachment Table
wb_photo
Contains:
igis_id
photoname
image metadata
Image Folder
H:\aura\photos
Contains actual image files:
1.jpg
2.jpg
3.jpg
Final Output
The script generates:
waterbody.gdb
Containing:
wb_main
wb_main__ATTACH
wb_main__ATTACHREL
and:
waterbody.zip
which can be published to ArcGIS Enterprise.
Prerequisites
Software Required
ArcGIS Pro
PostgreSQL
PostGIS Extension
ArcPy
Important Database Requirement
ArcGIS Pro requires a real geometry column.
Latitude and longitude fields alone are NOT enough.
Your table must contain:
geom geometry(Point,4326)
Create Geometry Column in PostGIS
Step 1 — Add Geometry Column
ALTER TABLE wb_main
ADD COLUMN geom geometry(Point,4326);
Step 2 — Populate Geometry
UPDATE wb_main
SET geom = ST_SetSRID(
ST_MakePoint(longitude, latitude),
4326
)
WHERE longitude IS NOT NULL
AND latitude IS NOT NULL;
Step 3 — Create Spatial Index
CREATE INDEX idx_wb_main_geom
ON wb_main
USING GIST(geom);
Verify in ArcGIS Pro
After reconnecting the .sde connection:
aura.public.wb_main
must appear as:
Feature Class
NOT:
Database Table
Create Database Connection (.sde)
Inside ArcGIS Pro:
Catalog Pane
→ Databases
→ New Database Connection
Configuration:
| Setting | Value |
|---|---|
| Platform | PostgreSQL |
| Instance | localhost,5432 |
| Authentication | Database Authentication |
| Username | postgres |
| Database | aura |
Save the connection.
Example:
H:\arcpro\gdb_bild\localhost.sde
Final ArcPy Script
import arcpy
import os
import csv
import shutil
# =========================
# CONFIG
# =========================
# SDE Connection
sde_conn = r"H:\arcpro\gdb_bild\localhost.sde"
# Feature Class
feature_class = os.path.join(
sde_conn,
"aura.public.wb_main"
)
# Photo Table
photo_table = os.path.join(
sde_conn,
"aura.public.wb_photo"
)
# Folder containing actual image files
image_folder = r"H:\aura\photos"
# Output Folder
output_folder = r"H:\aura\output"
# Output GDB Name
gdb_name = "waterbody.gdb"
# ZIP File Name
zip_name = "waterbody"
# Temporary CSV for attachment mapping
match_table_csv = os.path.join(
output_folder,
"attachment_match.csv"
)
# =========================
# CREATE OUTPUT FOLDER
# =========================
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# =========================
# CREATE FILE GDB
# =========================
gdb_path = os.path.join(output_folder, gdb_name)
# Delete old GDB if exists
if arcpy.Exists(gdb_path):
arcpy.management.Delete(gdb_path)
print("Old GDB deleted.")
# Create new GDB
arcpy.management.CreateFileGDB(
output_folder,
gdb_name
)
print("File GDB created.")
# =========================
# EXPORT FEATURE CLASS
# =========================
output_fc = os.path.join(
gdb_path,
"wb_main"
)
# Export feature class
arcpy.conversion.FeatureClassToFeatureClass(
feature_class,
gdb_path,
"wb_main"
)
print("Feature class exported.")
# =========================
# ADD GLOBAL IDS
# =========================
arcpy.management.AddGlobalIDs(output_fc)
print("Global IDs added.")
# =========================
# ENABLE ATTACHMENTS
# =========================
arcpy.management.EnableAttachments(output_fc)
print("Attachments enabled.")
# =========================
# BUILD MATCH TABLE CSV
# =========================
with open(match_table_csv, "w", newline="") as csvfile:
writer = csv.writer(csvfile)
# CSV Header
writer.writerow([
"igis_id",
"photo_path"
])
# Read photo table
with arcpy.da.SearchCursor(
photo_table,
["igis_id", "photoname"]
) as cursor:
for row in cursor:
igis_id = row[0]
photoname = row[1]
# Build image path
filepath = os.path.join(
image_folder,
photoname
)
print(f"Checking: {filepath}")
# Check image exists
if os.path.exists(filepath):
writer.writerow([
igis_id,
filepath
])
print(f"Added: {filepath}")
else:
print(f"Missing file: {filepath}")
print("Attachment match table created.")
# =========================
# ADD ATTACHMENTS
# =========================
arcpy.management.AddAttachments(
in_dataset=output_fc,
in_join_field="igis_id",
in_match_table=match_table_csv,
in_match_join_field="igis_id",
in_match_path_field="photo_path"
)
print("Attachments added successfully.")
# =========================
# DELETE TEMP CSV
# =========================
if os.path.exists(match_table_csv):
os.remove(match_table_csv)
print("Temporary CSV deleted.")
# =========================
# CREATE ZIP FILE
# =========================
zip_output = os.path.join(
output_folder,
zip_name
)
# Delete old zip if exists
if os.path.exists(zip_output + ".zip"):
os.remove(zip_output + ".zip")
print("Old ZIP deleted.")
# Create ZIP
shutil.make_archive(
zip_output,
'zip',
output_folder,
gdb_name
)
print("ZIP file created.")
# =========================
# FINAL OUTPUT
# =========================
print("DONE")
print(f"GDB Path: {gdb_path}")
print(f"ZIP Path: {zip_output}.zip")
Run Script in ArcGIS Pro
Option 1 — Python Window
Inside ArcGIS Pro:
View
→ Python Window
Paste and run the script.
Option 2 — Notebook
Insert
→ New Notebook
Run script cells.
Publishing to ArcGIS Enterprise
IMPORTANT
Do NOT upload the ZIP directly from Portal.
Recommended workflow:
ArcGIS Pro
→ Share As Web Layer
Publishing Steps
Step 1
Add:
wb_main
from the generated GDB into the map.
Step 2
Verify attachments work locally.
Click features and confirm images appear.
Step 3
Enable unique numeric IDs.
Map Properties
→ General
→ Allow assignment of unique numeric IDs
Step 4
Share:
Sharing
→ Share As Web Layer
Step 5
Configuration:
| Setting | Value |
|---|---|
| Layer Type | Feature |
| Data Option | Copy all data |
| Enable Attachments | Yes |
Common Errors
ERROR 000732
Dataset does not exist or is not supported
Cause:
Table is not spatial
Missing geometry column
Fix:
Create PostGIS geometry column
ERROR 00374
Unique numeric IDs are not assigned
Fix:
Map Properties
→ Enable unique numeric IDs
ERROR 00231
Layer's data source must be registered with the server
Fix:
Use:
Copy all data
instead of:
Reference registered data
Final Result
You now have:
PostgreSQL/PostGIS spatial data
ArcGIS Pro integration
File Geodatabase export
Image attachments
ZIP packaging
ArcGIS Enterprise publishing workflow
with fully working hosted feature layer attachments.
Output Structure
output/
│
├── waterbody.gdb
│ ├── wb_main
│ ├── wb_main__ATTACH
│ └── wb_main__ATTACHREL
│
└── waterbody.zip
Conclusion
This workflow provides a complete production-ready pipeline for exporting PostGIS data with image attachments into ArcGIS-compatible File Geodatabases and publishing them to ArcGIS Enterprise.
The same approach can also be extended for:
Survey applications
Asset management systems
Environmental monitoring
Utility inspection systems
Waterbody inventory projects
Mobile GIS workflows
