1 | import * as azure from 'azure-storage';
|
2 | import * as fs from 'fs';
|
3 |
|
4 | export interface IContainerConfig {
|
5 | name: string;
|
6 | isPublic: boolean;
|
7 | expiry?: number;
|
8 | }
|
9 |
|
10 | export default class FileStorage {
|
11 |
|
12 | private blob: any;
|
13 | private containers: IContainerConfig[];
|
14 |
|
15 | constructor(
|
16 | storageAccountOrConnectionString: string,
|
17 | storageAccessKey: string,
|
18 | host: string | null,
|
19 | ) {
|
20 | this.blob = azure.createBlobService(
|
21 | storageAccountOrConnectionString,
|
22 | storageAccessKey,
|
23 | host,
|
24 | );
|
25 | }
|
26 |
|
27 | |
28 |
|
29 |
|
30 |
|
31 | public createContainers = async (containers: IContainerConfig[]) => {
|
32 | this.containers = containers;
|
33 |
|
34 | const createContainer = (containerConfig) => {
|
35 | return new Promise((resolve, reject) => {
|
36 | this.blob.createContainerIfNotExists(containerConfig.name, {
|
37 | publicAccessLevel: containerConfig.isPublic ? 'blob' : '',
|
38 | }, (err: any) => {
|
39 | if (err) {
|
40 | reject(err);
|
41 | } else {
|
42 | resolve('done');
|
43 | }
|
44 | });
|
45 | });
|
46 | };
|
47 |
|
48 | const promises = containers.map(container => createContainer(container));
|
49 |
|
50 | try {
|
51 | await Promise.all(promises);
|
52 | return {status: 'done'};
|
53 | } catch (err) {
|
54 | return {status: 'done'};
|
55 | }
|
56 | }
|
57 |
|
58 | public create = async (
|
59 | containerName: string,
|
60 | filename: string,
|
61 | file: string,
|
62 | ) => {
|
63 |
|
64 | const container = this.getContainerByName(containerName);
|
65 |
|
66 | const blob = await this.doCreateBlob(container.name, filename, file);
|
67 |
|
68 | if (container.isPublic) {
|
69 | return this.getPublicUrl(container.name, filename);
|
70 | } else {
|
71 | return blob;
|
72 | }
|
73 |
|
74 | }
|
75 |
|
76 | public remove = async (
|
77 | containerName: string,
|
78 | filename: string,
|
79 | ) => (
|
80 | this.doRemoveBlob(containerName, filename)
|
81 | )
|
82 |
|
83 | public replace = async (
|
84 | containerName: string,
|
85 | filename: string,
|
86 | file: string,
|
87 | ) => {
|
88 | const container = this.getContainerByName(containerName);
|
89 |
|
90 | await this.doRemoveBlob(containerName, filename);
|
91 | const blob = await this.doCreateBlob(container.name, filename, file);
|
92 |
|
93 | if (container.isPublic) {
|
94 | return this.getPublicUrl(container.name, filename);
|
95 | } else {
|
96 | return blob;
|
97 | }
|
98 | }
|
99 |
|
100 | public getPublicUrl = async (
|
101 | containerName: string,
|
102 | filename: string,
|
103 | ) => (
|
104 | this.blob.getUrl(containerName, filename)
|
105 | )
|
106 |
|
107 | public getPrivateUrl = async (
|
108 | containerName: string,
|
109 | filename: string,
|
110 | ) => {
|
111 |
|
112 | if (!filename) {
|
113 | return null;
|
114 | }
|
115 |
|
116 | const container = this.getContainerByName(containerName);
|
117 |
|
118 | const startDate = new Date();
|
119 | const expiryDate = new Date(startDate);
|
120 | startDate.setMilliseconds(startDate.getMilliseconds() - 10000);
|
121 | expiryDate.setMinutes(startDate.getMinutes() + container.expiry);
|
122 |
|
123 | const sharedAccessPolicy = {
|
124 | AccessPolicy: {
|
125 | Permissions: azure.BlobUtilities.SharedAccessPermissions.READ,
|
126 | Start: startDate,
|
127 | Expiry: expiryDate,
|
128 | },
|
129 | };
|
130 |
|
131 | const token = this.blob.generateSharedAccessSignature(containerName, filename, sharedAccessPolicy);
|
132 |
|
133 | return this.blob.getUrl(containerName, filename, token);
|
134 | }
|
135 |
|
136 | public generateSasToken(
|
137 | containerName: string,
|
138 | blobName: string,
|
139 | permissions: string,
|
140 | ) {
|
141 | const connString = process.env.AzureWebJobsStorage;
|
142 | const blobService = azure.createBlobService(connString);
|
143 |
|
144 |
|
145 |
|
146 | const startDate = new Date();
|
147 | startDate.setMinutes(startDate.getMinutes() - 5);
|
148 | const expiryDate = new Date(startDate);
|
149 | expiryDate.setMinutes(startDate.getMinutes() + 60);
|
150 |
|
151 | permissions = permissions || azure.BlobUtilities.SharedAccessPermissions.WRITE;
|
152 |
|
153 | const sharedAccessPolicy = {
|
154 | AccessPolicy: {
|
155 | Permissions: permissions,
|
156 | Start: startDate,
|
157 | Expiry: expiryDate,
|
158 | },
|
159 | };
|
160 |
|
161 | const sasToken = blobService.generateSharedAccessSignature(containerName, blobName, sharedAccessPolicy);
|
162 |
|
163 | return {
|
164 | token: sasToken,
|
165 | uri: blobService.getUrl(containerName, blobName, sasToken, true),
|
166 | };
|
167 | }
|
168 |
|
169 | |
170 |
|
171 |
|
172 |
|
173 | private doCreateBlob = async (
|
174 | containerName: string,
|
175 | filename: string,
|
176 | file: string,
|
177 | ) => (
|
178 | new Promise((resolve, reject) => {
|
179 |
|
180 | const stream = fs.createReadStream(file);
|
181 | const filesize = fs.statSync(file).size;
|
182 |
|
183 | this.blob.createBlockBlobFromStream(containerName, filename, stream, filesize, (err, result) => {
|
184 | if (err) {
|
185 | reject(err);
|
186 | } else {
|
187 | resolve(result);
|
188 | }
|
189 | });
|
190 | })
|
191 | )
|
192 |
|
193 | private doRemoveBlob = async (
|
194 | containerName: string,
|
195 | filename: string,
|
196 | ) => (
|
197 | new Promise((resolve, reject) => {
|
198 | this.blob.deleteBlobIfExists(containerName, filename, (err, result) => {
|
199 | if (err) {
|
200 | reject(err);
|
201 | } else {
|
202 | resolve(result);
|
203 | }
|
204 | });
|
205 | })
|
206 | )
|
207 |
|
208 | private getContainerByName = (name: string) => {
|
209 | let result: IContainerConfig | null = null;
|
210 | this.containers.forEach(container => {
|
211 | if (container.name === name) {
|
212 | result = container;
|
213 | }
|
214 | });
|
215 | return result;
|
216 | }
|
217 |
|
218 | }
|