Skip to content

Commit

Permalink
Fix: Added the content-disposition header (#27521)
Browse files Browse the repository at this point in the history
In this PR I've added the `Content-Disposition` header to the response of the image `/_next/image` route. That header is used by the browser when showing the dialog for the `Save image as...` action. 

There are some differences between the browsers, ex:
When requesting the image `test.jpg`,  the response header `Content-Type: image/webp` - in FF the filename from the `Save image as...` dialog would be `test.webp` but in Chrome `test.jpg` even if the `Content-Disposition: inline; filename="test.webp"` is present in the headers.  The same about png images, the rest types are fine.  It looks like FF is checking the `Content-Type` for the extension but the Chrome does not and is doing another type of check.

Fixes #26737

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

## Documentation / Examples

- [x] Make sure the linting passes
  • Loading branch information
LetItRock authored Jul 27, 2021
1 parent 1af7892 commit 36b81f9
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 3 deletions.
28 changes: 28 additions & 0 deletions packages/next/server/image-optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export async function imageOptimizer(
const result = setResponseHeaders(
req,
res,
url,
etag,
maxAge,
contentType,
Expand Down Expand Up @@ -310,6 +311,7 @@ export async function imageOptimizer(
sendResponse(
req,
res,
url,
maxAge,
upstreamType,
upstreamBuffer,
Expand Down Expand Up @@ -430,6 +432,7 @@ export async function imageOptimizer(
sendResponse(
req,
res,
url,
maxAge,
contentType,
optimizedBuffer,
Expand All @@ -443,6 +446,7 @@ export async function imageOptimizer(
sendResponse(
req,
res,
url,
maxAge,
upstreamType,
upstreamBuffer,
Expand Down Expand Up @@ -473,9 +477,25 @@ async function writeToCacheDir(
await promises.writeFile(filename, buffer)
}

function getFileNameWithExtension(
url: string,
contentType: string | null
): string | void {
const [urlWithoutQueryParams] = url.split('?')
const fileNameWithExtension = urlWithoutQueryParams.split('/').pop()
if (!contentType || !fileNameWithExtension) {
return
}

const [fileName] = fileNameWithExtension.split('.')
const extension = getExtension(contentType)
return `${fileName}.${extension}`
}

function setResponseHeaders(
req: IncomingMessage,
res: ServerResponse,
url: string,
etag: string,
maxAge: number,
contentType: string | null,
Expand All @@ -496,12 +516,19 @@ function setResponseHeaders(
if (contentType) {
res.setHeader('Content-Type', contentType)
}

const fileName = getFileNameWithExtension(url, contentType)
if (fileName) {
res.setHeader('Content-Disposition', `inline; filename="${fileName}"`)
}

return { finished: false }
}

function sendResponse(
req: IncomingMessage,
res: ServerResponse,
url: string,
maxAge: number,
contentType: string | null,
buffer: Buffer,
Expand All @@ -512,6 +539,7 @@ function sendResponse(
const result = setResponseHeaders(
req,
res,
url,
etag,
maxAge,
contentType,
Expand Down
Loading

0 comments on commit 36b81f9

Please sign in to comment.