Skip to content

Commit 3f30675

Browse files
authored
Merge pull request #3058 from SeedCompany/file-url-download-or-preview
2 parents d66b752 + 28663be commit 3f30675

File tree

6 files changed

+47
-34
lines changed

6 files changed

+47
-34
lines changed

src/components/file/file-url.controller.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
HttpStatus,
66
Inject,
77
Param,
8+
Query,
89
Request,
910
Response,
1011
} from '@nestjs/common';
@@ -29,6 +30,7 @@ export class FileUrlController {
2930
@Get(':fileId/:fileName?')
3031
async download(
3132
@Param('fileId') fileId: ID,
33+
@Query('download') download: '' | undefined,
3234
@Request() request: IRequest,
3335
@Response() res: unknown,
3436
) {
@@ -41,7 +43,7 @@ export class FileUrlController {
4143

4244
// TODO authorization using session
4345

44-
const url = await this.files.getDownloadUrl(node);
46+
const url = await this.files.getDownloadUrl(node, download != null);
4547
const cacheControl = this.files.determineCacheHeader(node);
4648

4749
const { httpAdapter } = this.httpAdapterHost;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Args, ResolveField } from '@nestjs/graphql';
2+
import { stripIndent } from 'common-tags';
3+
import { URL } from 'url';
4+
5+
export const Resolver = () =>
6+
ResolveField(() => URL, {
7+
description: stripIndent`
8+
A url to the file.
9+
10+
This url could require authentication.
11+
`,
12+
});
13+
14+
export const DownloadArg = () =>
15+
Args('download', {
16+
description: stripIndent`
17+
Whether the browser should download this file if opened directly
18+
19+
This sets the \`Content-Disposition\` header to \`attachment\` instead of \`inline\`.
20+
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
21+
`,
22+
defaultValue: false,
23+
});

src/components/file/file-version.resolver.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,19 @@ import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
22
import { stripIndent } from 'common-tags';
33
import { URL } from 'url';
44
import { FileVersion } from './dto';
5+
import * as FileUrl from './file-url.resolver-util';
56
import { FileService } from './file.service';
67

78
@Resolver(FileVersion)
89
export class FileVersionResolver {
910
constructor(protected readonly service: FileService) {}
1011

11-
@ResolveField(() => URL, {
12-
description: stripIndent`
13-
A url to the file version.
14-
15-
This url could require authentication.
16-
`,
17-
})
18-
async url(@Parent() node: FileVersion) {
19-
return await this.service.getUrl(node);
12+
@FileUrl.Resolver()
13+
async url(
14+
@Parent() node: FileVersion,
15+
@FileUrl.DownloadArg() download: boolean,
16+
) {
17+
return await this.service.getUrl(node, download);
2018
}
2119

2220
@ResolveField(() => URL, {

src/components/file/file.resolver.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
RequestUploadOutput,
3333
} from './dto';
3434
import { FileNodeLoader } from './file-node.loader';
35+
import * as FileUrl from './file-url.resolver-util';
3536
import { FileService } from './file.service';
3637

3738
@Resolver(File)
@@ -75,15 +76,9 @@ export class FileResolver {
7576
return await this.service.listChildren(node, input, session);
7677
}
7778

78-
@ResolveField(() => URL, {
79-
description: stripIndent`
80-
A url to the file.
81-
82-
This url could require authentication.
83-
`,
84-
})
85-
async url(@Parent() node: File) {
86-
return await this.service.getUrl(node);
79+
@FileUrl.Resolver()
80+
async url(@Parent() node: File, @FileUrl.DownloadArg() download: boolean) {
81+
return await this.service.getUrl(node, download);
8782
}
8883

8984
@ResolveField(() => URL, {

src/components/file/file.service.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,27 +145,28 @@ export class FileService {
145145
return data;
146146
}
147147

148-
async getUrl(node: FileNode) {
148+
async getUrl(node: FileNode, download: boolean) {
149149
const url = withAddedPath(
150150
this.config.hostUrl,
151151
FileUrl.path,
152152
isFile(node) ? node.latestVersionId : node.id,
153153
encodeURIComponent(node.name),
154154
);
155-
return url.toString();
155+
return url.toString() + (download ? '?download' : '');
156156
}
157157

158-
async getDownloadUrl(node: FileNode): Promise<string> {
158+
async getDownloadUrl(node: FileNode, download = true): Promise<string> {
159159
if (isDirectory(node)) {
160160
throw new InputException('View directories via GraphQL API');
161161
}
162162
const id = isFile(node) ? node.latestVersionId : node.id;
163+
const disposition = download ? 'attachment' : 'inline';
163164
try {
164165
// before sending link, first check if object exists in s3
165166
await this.bucket.headObject(id);
166167
return await this.bucket.getSignedUrl(GetObject, {
167168
Key: id,
168-
ResponseContentDisposition: `attachment; filename="${encodeURIComponent(
169+
ResponseContentDisposition: `${disposition}; filename="${encodeURIComponent(
169170
node.name,
170171
)}"`,
171172
ResponseContentType: node.mimeType,
Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
1-
import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
1+
import { Parent, Resolver } from '@nestjs/graphql';
22
import { Loader, LoaderOf } from '@seedcompany/data-loader';
3-
import { stripIndent } from 'common-tags';
4-
import { URL } from 'url';
53
import { FileNodeLoader } from './file-node.loader';
4+
import * as FileUrl from './file-url.resolver-util';
65
import { FileService } from './file.service';
76
import { Media } from './media/media.dto';
87

98
@Resolver(() => Media)
109
export class MediaUrlResolver {
1110
constructor(protected readonly service: FileService) {}
1211

13-
@ResolveField(() => URL, {
14-
description: stripIndent`
15-
A url to the file version.
16-
17-
This url could require authentication.
18-
`,
19-
})
12+
@FileUrl.Resolver()
2013
async url(
2114
@Parent() media: Media,
15+
@FileUrl.DownloadArg() download: boolean,
2216
@Loader(FileNodeLoader) files: LoaderOf<FileNodeLoader>,
2317
) {
2418
const node = await files.load(media.file);
25-
return await this.service.getUrl(node);
19+
return await this.service.getUrl(node, download);
2620
}
2721
}

0 commit comments

Comments
 (0)