Add Detail View and File Links for a More Complete Upload Dashboard
Add a detail view for uploaded files and return presigned S3 URLs to allow safe, temporary access to private files.
All services used in this lesson are covered by the AWS Free Tier.
AWS Services Used
Learning Outcomes
By the end of this lesson, you will be able to:
- Add a detail view for one uploaded file.
- Return a presigned S3 URL so a private file can be opened safely from the browser.
- Explain why presigned URLs are better than making the whole bucket public.
- Update the frontend to show file details and a temporary download/view link.
- Explain why the Lambda function that generates the presigned URL must have permission to read the object.
The Core Idea
Your upload bucket has been private so far, and that is the right default. By default, Amazon S3 objects are private, and one standard way to share access temporarily is to create a presigned URL.
A presigned URL uses the creator’s AWS credentials to grant time-limited permission to access an object, and the URL can be opened in a browser. This means you do not need to make the bucket public just to let a user open one file.
Why presigned URLs are the right fit here
A presigned URL is ideal for this lesson because:
- Your objects stay private by default.
- The browser can still open one selected file.
- Access is temporary, not permanent.
- You do not need a public bucket policy for every object.
Part 1: Upgrade the Detail Lambda Function
You already built a read endpoint in a previous lesson. Now upgrade that Lambda so it returns:
- The metadata item from DynamoDB.
- A presigned S3 URL for the object.
Add an environment variable URL_EXPIRES_SECONDS=900.
Use this Python code:
import json
import os
import boto3
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table(os.environ["TABLE_NAME"])
s3_client = boto3.client("s3")
URL_EXPIRES_SECONDS = int(os.environ.get("URL_EXPIRES_SECONDS", "900"))
def lambda_handler(event, context):
query = event.get("queryStringParameters") or {}
bucket = query.get("bucket")
object_key = query.get("key")
if not bucket or not object_key:
return {
"statusCode": 400,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({
"error": "Missing required query parameters: bucket and key"
})
}
response = table.get_item(
Key={
"bucket": bucket,
"object_key": object_key
}
)
item = response.get("Item")
if not item:
return {
"statusCode": 404,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({
"error": "Metadata not found"
})
}
download_url = s3_client.generate_presigned_url(
"get_object",
Params={
"Bucket": bucket,
"Key": object_key
},
ExpiresIn=URL_EXPIRES_SECONDS
)
result = {
"item": item,
"download_url": download_url,
"expires_in": URL_EXPIRES_SECONDS
}
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(result)
}
Part 2: Update the Lambda Execution Role
The Lambda function needs permission to perform s3:GetObject on the uploaded object, because presigned URLs use the permissions of the AWS principal that generated them.
Add this statement to your role:
{
"Sid": "ReadUploadedObjects",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::YOUR_UPLOAD_BUCKET/*"
}
Part 3: Update the Frontend
Update your frontend so each listed file can load details and show an “Open file” link.
Add a detail section to your HTML:
<div id="detail"></div>
And update your JavaScript to include a loadDetail function that fetches from your existing /metadata endpoint (now upgraded with the download URL).
Part 4: Test the Flow
- Open the S3-hosted frontend.
- Search using your bucket and optional prefix.
- Click View details on one file.
- Confirm the detail panel appears with an Open file link.
- Verify the file opens while the bucket remains private.
Lab Checklist
| Step | Success Condition |
|---|---|
| Update detail Lambda | Function returns metadata plus download_url |
| Add s3:GetObject permission | Lambda can generate working presigned URLs |
| Update frontend | Detail panel with "Open file" link appears |
| Click open link | File opens using temporary URL |
| Keep bucket private | No public bucket policy is needed |
Micro-activity 1: Reflection
Think about it
After the lab, reflect: Which object key did you open? Did the detail endpoint return a download_url? How many seconds was the link valid for? Did the file open without making the bucket public?
Micro-activity 2: Presigned URL Concepts
Match each presigned URL concept to its description
Examples
Choose one, then match it on the right
Characteristics
Select an example first
0 of 4 matched so far.
Summary
In this lesson, you upgraded a simple list page into a more complete private upload dashboard. The backend now returns both metadata and a temporary S3 access link, and the frontend lets the user open a file without exposing the entire bucket.