Google Cloud Storage Подписанные URL - Как указать максимальный размер файла?
Цель
Мы хотим, чтобы пользователи могли загружать изображения в Google Cloud Storage.
проблема
Мы могли бы достичь этого косвенно с нашим сервером в качестве посредника - сначала пользователь загружает данные на наш сервер, затем наш привилегированный сервер может загружать данные в облачное хранилище.
Однако мы считаем, что это неоправданно медленно, и вместо этого хотели бы, чтобы пользователь загружал данные непосредственно в облачное хранилище.
Предложенное решение
Для прямой загрузки мы генерируем подписанный URL на нашем сервере. Подписанный URL-адрес указывает срок действия и может использоваться только с глаголом HTTP PUT. Пользователь может запросить подписанный URL, а затем - только в течение ограниченного времени - загрузить изображение по пути, указанному в подписанном URL.
Проблема с решением
Есть ли способ обеспечить максимальный размер загружаемого файла? Очевидно, что мы бы хотели, чтобы пользователи не пытались загружать файлы размером 20 ГБ, когда мы ожидаем, что файлы размером менее 1 МБ.
Кажется, что это очевидная уязвимость, но я не знаю, как ее устранить, все еще используя Signed URL.
Кажется, есть способ сделать это, используя документы политики ( ответ на переполнение стека), но этому вопросу уже более 2 лет.
5 ответов
Всем, кто смотрит на тент сегодня, следует помнить, что
x-goog-content-length-range:0,25000
- допустимый способ ограничить размер загрузки от 0 до 25000 байтов для
PUT
X-Upload-Content-Length
не будет работать, и вы все равно сможете загружать файлы большего размера
Подписание длины содержимого должно помочь.
Google Cloud не разрешает загрузку файлов большего размера, даже если длина содержимого установлена на меньшее значение.
Вот как должны выглядеть параметры подписанного URL:
const writeOptions: GetSignedUrlConfig = {
version: 'v4',
action: 'write',
expires: Date.now() + 900000, // 15 minutes
extensionHeaders: {
"content-length": length // desired length in bytes
}
}
Политические документы по-прежнему правильный ответ. Они описаны здесь: https://cloud.google.com/storage/docs/xml-api/post-object
Важная часть политического документа, который вам понадобится:
["content-length-range", <min_range>, <max_range>].
Мой рабочий код в NodeJS соответствовал https://blog.koliseo.com/limit-the-size-of-uploaded-files-with-signed-urls-on-google-cloud-storage/. Вы должны использовать версию
v4
public async getPreSignedUrlForUpload(
fileName: string,
contentType: string,
size: number,
bucketName: string = this.configService.get('DEFAULT_BUCKET_NAME'),
): Promise<string> {
const bucket = this.storage.bucket(bucketName);
const file = bucket.file(fileName);
const response = await file.getSignedUrl({
action: 'write',
contentType,
extensionHeaders: {
'X-Upload-Content-Length': size,
},
expires: Date.now() + 60 * 1000, // 1 minute
version: 'v4',
});
const signedUrl = this.maskSignedUrl(response[0], bucketName);
return signedUrl;
}
Во Frontend мы должны установить такое же количество Size в заголовке
X-Upload-Content-Length
export async function uploadFileToGCP(
signedUrl: string,
file: any
): Promise<any> {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.withCredentials = process.env.NODE_ENV === 'production';
xhr.addEventListener('readystatechange', function () {
if (this.readyState === 4) {
resolve(this.responseText);
}
});
xhr.open('PUT', signedUrl, true);
xhr.setRequestHeader('Content-Type', file.type);
xhr.setRequestHeader('X-Upload-Content-Length', file.size);
xhr.send(file);
});
}
А также не забудьте настроить
responseHeader
в
GS CORS
gsutil cors get gs://asia-item-images
[{"maxAgeSeconds": 3600, "method": ["GET", "OPTIONS", "PUT"], "origin": ["*"], "responseHeader": ["Content-Type", "Access-Control-Allow-Origin", "X-Upload-Content-Length", "X-Goog-Resumable"]}]
Вы можете использовать
X-Upload-Content-Length
вместо
Content-Length
. См. Сообщение в блоге здесь.
На стороне сервера (Java):
Map<String, String> extensionHeaders = new HashMap<>();
extensionHeaders.put("X-Upload-Content-Length", "" + contentLength);
extensionHeaders.put("Content-Type", "application/octet-stream");
var url =
storage.signUrl(
blobInfo,
15,
TimeUnit.MINUTES,
Storage.SignUrlOption.httpMethod(HttpMethod.PUT),
Storage.SignUrlOption.withExtHeaders(extensionHeaders),
Storage.SignUrlOption.withV4Signature()
);
На стороне клиента (машинописный текст):
const response = await fetch(url, {
method: 'PUT',
headers: {
'X-Upload-Content-Length': `${file.size}`,
'Content-Type': 'application/octet-stream',
},
body: file,
});
Вам нужно будет настроить политику cors для своего ведра:
[
{
"origin": ["https://your-website.com"],
"responseHeader": [
"Content-Type",
"Access-Control-Allow-Origin",
"X-Upload-Content-Length",
"x-goog-resumable"
],
"method": ["PUT", "OPTIONS"],
"maxAgeSeconds": 3600
}
]