feat(storage): inline preview for json/csv/log/text files
Add a /preview endpoint that range-reads the first chunk of an object and returns it as text. Browser gets an eye action (list + gallery) opening a modal that pretty-prints JSON, renders CSV/TSV as a table, and shows logs/ text/yaml/xml verbatim, with a truncation note and download link.
This commit is contained in:
@@ -257,6 +257,39 @@ async def download_object(bucket: str, key: str = Query(...), inline: bool = Que
|
||||
return JSONResponse({"ok": False, "error": str(exc)}, status_code=404)
|
||||
|
||||
|
||||
@router.get("/buckets/{bucket}/preview")
|
||||
async def preview_object(bucket: str, key: str = Query(...), max_bytes: int = Query(131072, le=1048576)):
|
||||
"""Return the first chunk of an object as text for inline preview."""
|
||||
try:
|
||||
_track("preview")
|
||||
s3 = _client()
|
||||
head = None
|
||||
try:
|
||||
head = s3.head_object(Bucket=bucket, Key=key)
|
||||
except Exception:
|
||||
pass
|
||||
total = int(head.get("ContentLength", 0)) if head else 0
|
||||
obj = s3.get_object(Bucket=bucket, Key=key, Range=f"bytes=0-{max_bytes - 1}")
|
||||
raw = obj["Body"].read()
|
||||
truncated = (total > len(raw)) or (len(raw) >= max_bytes)
|
||||
text = raw.decode("utf-8", errors="replace")
|
||||
return {
|
||||
"ok": True,
|
||||
"bucket": bucket,
|
||||
"key": key,
|
||||
"ext": _ext(key),
|
||||
"size": total or len(raw),
|
||||
"size_human": _human_size(total or len(raw)),
|
||||
"bytes_read": len(raw),
|
||||
"truncated": truncated,
|
||||
"content": text,
|
||||
}
|
||||
except ClientError as exc:
|
||||
return JSONResponse({"ok": False, "error": str(exc)}, status_code=404)
|
||||
except Exception as exc:
|
||||
return JSONResponse({"ok": False, "error": str(exc)}, status_code=502)
|
||||
|
||||
|
||||
# ── Analytics ────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user