Введение
Сервис экспорта должен удалять временные загрузки, не удаляя документы, которые нужно сохранить. Вы примените правила истечения срока хранения для заданного префикса, перехода между классами хранения и очистки незавершённых загрузок в новом приватном бакете, а затем проверите фактическую политику и метаданные объектов.
Сначала завершите настройку управления объектами и очистку multipart-загрузок. На этой новой виртуальной машине установлены Node.js 22.22.0, Wrangler 4.131.1 и AWS SDK 3.888.0. R2 должен быть активирован, а у вас должно быть разрешение на настройку нового бакета. Изучите сведения о жизненном цикле объектов и тарифах, включая минимальный срок хранения и плату за извлечение данных для Infrequent Access. Тестовый объект останется в классе Standard и будет явно удалён во время этой сессии. При проверке оцениваются применённые правила и текущие метаданные, а не удаление или переход через несколько дней. Домен не требуется.
Создайте приватный бакет для документов
На этом шаге вы авторизуете виртуальную машину и создадите один временный бакет. Авторизация устройства подтверждает вашу учебную учётную запись. Для управления бакетом R2 используется отдельный API-токен, ограниченный этой учётной записью.
Запустите Bash для выполнения команд ниже, затем перейдите в подготовленный проект и проверьте установленные инструменты. Не закрывайте этот терминал: в нём должны сохраняться переменные с именами ресурсов.
bash
cd /home/labex/project/r2-lab
export PATH="$PWD/.tools/node-v22.22.0-linux-x64/bin:$PATH"
node --version
npx wrangler --version
Откройте в своём браузере страницу авторизации и введите показанный код устройства. Перед подтверждением проверьте учебную учётную запись, а также запрошенные области доступа для чтения данных учётной записи и пользователя:
npx wrangler login --device --browser=false --scopes account:read user:read
npx wrangler whoami --json
Убедитесь, что значение loggedIn равно true. Прочитайте имя учётной записи, даже если в списке указана только одна. В следующей команде замените YOUR_ACCOUNT_ID фактическим идентификатором этой учётной записи из 32 символов. Команда openssl rand -hex 6 создаёт двенадцать случайных шестнадцатеричных символов, поэтому имя ресурса этого запуска не совпадёт с именем предыдущего запуска. Here-документ записывает стандартный файл конфигурации, а оболочка подставляет в него значения переменных.
ACCOUNT_ID=YOUR_ACCOUNT_ID
RUN_ID=$(openssl rand -hex 6)
NAME="labex-c05-r07-$RUN_ID"
BUCKET="$NAME-docs"
cat > wrangler.jsonc <<JSON
{"name":"$NAME","account_id":"$ACCOUNT_ID","compatibility_date":"2026-07-30","r2_buckets":[{"binding":"DOCUMENTS","bucket_name":"$BUCKET"}]}
JSON
Для управления бакетом откройте на странице профиля Cloudflare раздел API Tokens и создайте пользовательский токен с именем этого задания. Выдайте разрешение Account → Workers R2 Storage → Edit и в разделе Account Resources ограничьте токен учебной учётной записью, идентификатор которой вы сохранили. Установите короткий срок действия. Не добавляйте другие учётные записи или несвязанные разрешения. Этот токен управления предназначен для администрирования бакетов, включая их создание и удаление. Далее в этом же шаге вы создадите отдельный токен объектов с доступом только к этому бакету для работы с объектами через S3 SDK.
Один раз скопируйте токен в скрытый запрос виртуальной машины. umask 077 ограничивает доступ к файлу вашим пользователем, а read -s скрывает ввод. Файл использует стандартную переменную токена Wrangler и исключён из Git.
umask 077
read -r -s -p 'R2 management API token: ' R2_MANAGEMENT_TOKEN; printf '\n'
printf 'CLOUDFLARE_API_TOKEN=%s\n' "$R2_MANAGEMENT_TOKEN" > .env.management
unset R2_MANAGEMENT_TOKEN
Используйте --env-file=.env.management только для команд управления R2. Обычная команда whoami по-прежнему проверяет авторизацию устройства на виртуальной машине.
Размещайте --env-file в конце каждой команды Wrangler, чтобы имя команды не попадало в список аргументов с файлами. Если после создания каждого бакета Wrangler предложит добавить привязку в конфигурацию, введите n и нажмите Enter. Нужная привязка уже указана в конфигурации.
npx wrangler r2 bucket create "$BUCKET" --env-file=.env.management
Выведите список бакетов и найдите точное сгенерированное имя. Остальные бакеты относятся к другим заданиям — не изменяйте их.
npx wrangler r2 bucket list --env-file=.env.management
В Dashboard откройте Storage & databases → R2 → Overview, выберите именно этот бакет и проверьте, что список объектов пуст. В его настройках оставьте отключёнными URL для публичной разработки и пользовательские домены. Имя бакета в Dashboard подтверждает его идентичность; последующие проверки скачивания подтвердят сохранность байтов.
S3-совместимый API позволяет стандартным SDK работать с R2. Для этого используется отдельная пара ключей доступа, а не токен устройства Wrangler. В разделе R2 Overview выберите Account Details → API Tokens → Manage, затем создайте User API token с именем, содержащим сгенерированное имя ресурса этого задания. Выберите Object Read & Write, ограничьте токен именно этим новым бакетом и укажите короткий срок действия, если форма предлагает такую возможность. Не выбирайте все бакеты или доступ Admin. Не закрывайте страницу с токеном, пока не сохраните секрет, который отображается только один раз.
Используйте следующие запросы Bash в виртуальной машине. read -s скрывает ввод, а umask 077 делает файл с учётными данными доступным только вашему пользователю. Эти имена соответствуют стандартным переменным окружения AWS SDK. Вставьте Access Key ID и Secret Access Key в соответствующие запросы и нажмите Enter. Не вставляйте значение общего API-токена.
umask 077
read -r -s -p 'Access Key ID: ' AWS_ACCESS_KEY_ID; printf '\n'
read -r -s -p 'Secret Access Key: ' AWS_SECRET_ACCESS_KEY; printf '\n'
printf 'AWS_ACCESS_KEY_ID=%s\nAWS_SECRET_ACCESS_KEY=%s\n' "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" > .env.s3
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
Создайте повторно используемый стандартный клиент SDK. SDK требует строку региона; для R2 используется значение auto. Чтение существующей конфигурации гарантирует, что операции через CLI и SDK направлены к одной и той же учётной записи и одному и тому же бакету.
cat > storage.mjs <<'JS'
import { S3Client } from "@aws-sdk/client-s3";
import { readFileSync } from "node:fs";
const config = JSON.parse(readFileSync("wrangler.jsonc", "utf8"));
export const Bucket = config.r2_buckets[0].bucket_name;
export const s3 = new S3Client({
region: "auto",
endpoint: `https://${config.account_id}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});
JS
Примените правила жизненного цикла для заданных префиксов
На этом шаге вы настроите политику жизненного цикла — набор действий с хранилищем, которые R2 выполняет по мере старения объектов. Срок хранения временных экспортов должен истекать, а сохранённые руководства не должны подпадать под эти правила. Переход между классами хранения меняет класс тарификации и доступа, но не удаляет объект.
Этот новый временный бакет будет использовать два правила: срок хранения объектов temporary/ истекает через два дня, а незавершённые загрузки с этим префиксом прерываются через один день; объекты archive/ переходят в Infrequent Access через тридцать дней. Правила не распространяются на retained/.
API задаёт возраст в секундах: один день — это 86 400 секунд. Запишите политику с quoted here-документом, чтобы JSON сохранился без изменений:
cat > lifecycle.json <<'JSON'
{
"rules": [
{
"id": "temporary-exports",
"enabled": true,
"conditions": {
"prefix": "temporary/"
},
"deleteObjectsTransition": {
"condition": {
"type": "Age",
"maxAge": 172800
}
},
"abortMultipartUploadsTransition": {
"condition": {
"type": "Age",
"maxAge": 86400
}
}
},
{
"id": "archive-transition",
"enabled": true,
"conditions": {
"prefix": "archive/"
},
"storageClassTransitions": [
{
"condition": {
"type": "Age",
"maxAge": 2592000
},
"storageClass": "InfrequentAccess"
}
]
}
]
}
JSON
npx wrangler r2 bucket lifecycle set "$BUCKET" --file lifecycle.json --env-file=.env.management
npx wrangler r2 bucket lifecycle list "$BUCKET" --env-file=.env.management
Команда set заменяет всю политику, поэтому проверяйте только новый бакет этого задания. Убедитесь, что отображаются ровно два нужных префикса, включённое состояние и правильные значения возраста. Не применяйте эту замену к существующему рабочему бакету. В Dashboard откройте в том же бакете раздел Settings → Object Lifecycle Rules и проверьте действия, не изменяя их.
Ограничение по стоимости: для Infrequent Access взимается плата за извлечение данных и действует минимальный срок хранения. В этом задании настраивается будущий переход, а новые объекты Standard удаляются во время очистки. Вам не нужно ждать тридцать дней, принудительно выполнять переход или утверждать, что переход действительно произошёл.
На реальном экране Dashboard показаны настроенные будущие действия: удалять объекты temporary/ через 2 дня, прерывать незавершённые загрузки этого префикса через 1 день и переводить объекты archive/ в Infrequent Access через 30 дней. Это не доказывает, что сроки уже истекли или действия выполнены. Следующий шаг проверяет текущие метаданные; retained/ не входит ни в один из этих префиксов.

Проверьте метаданные недавно применённого истечения срока хранения
На этом шаге вы загрузите новые объекты после применения политики. В документации R2 указано, что новые объекты отражают применимое истечение срока хранения в x-amz-expiration; существующим объектам может потребоваться больше времени, чтобы отразить изменённое правило. SDK предоставляет этот заголовок как Expiration.
cat > seed.mjs <<'JS'
import { PutObjectCommand, HeadObjectCommand } from "@aws-sdk/client-s3";
import { readFileSync } from "node:fs";
import { s3, Bucket } from "./storage.mjs";
for (const Key of ["temporary/export.txt", "archive/export.txt"]) {
await s3.send(new PutObjectCommand({ Bucket, Key, Body: readFileSync("document.txt"), ContentType: "text/plain" }));
}
await s3.send(new PutObjectCommand({ Bucket, Key: "retained/handbook.txt", Body: readFileSync("retained.txt"), ContentType: "text/plain" }));
for (const Key of ["temporary/export.txt", "archive/export.txt", "retained/handbook.txt"]) {
const head = await s3.send(new HeadObjectCommand({ Bucket, Key }));
console.log({ key: Key, expiration: head.Expiration || "none", storageClass: head.StorageClass || "STANDARD" });
}
JS
node --env-file=.env.s3 seed.mjs
У объекта temporary/export.txt должна быть дата истечения срока хранения, у сохранённого руководства не должно быть срока удаления, а новый объект архива должен находиться в классе Standard. Будущий переход подтверждается удалённым правилом, а не текущим классом хранения IA. Если ожидаемые метаданные истечения срока хранения для нового объекта отсутствуют, проверьте применённый префикс и политику; не считайте такую проверку успешной.
Скачайте сохранённый объект и сравните его исходные байты:
npx wrangler r2 object get "$BUCKET/retained/handbook.txt" --remote --file retained-download.txt --env-file=.env.management
cmp retained.txt retained-download.txt
Успешное чтение политики и доступность сохранённого объекта подтверждают результат в пределах этого задания. Фактическое удаление по жизненному циклу выполняется асинхронно и может произойти после номинального истечения срока; это задание не проверяет событие, произошедшее через несколько часов.
Явно очистите хранилище задания
На этом шаге вы сразу удалите три тестовых объекта, не полагаясь на будущие действия жизненного цикла. В этом задании незавершённая загрузка не создавалась, но всё равно выведите этот список: один только список объектов не доказывает, что в бакете нет незавершённых частей загрузки.
cat > empty.mjs <<'JS'
import { DeleteObjectCommand, ListObjectsV2Command, ListMultipartUploadsCommand } from "@aws-sdk/client-s3";
import { s3, Bucket } from "./storage.mjs";
for (const Key of ["temporary/export.txt", "archive/export.txt", "retained/handbook.txt"]) await s3.send(new DeleteObjectCommand({ Bucket, Key }));
const objects = await s3.send(new ListObjectsV2Command({ Bucket }));
const uploads = await s3.send(new ListMultipartUploadsCommand({ Bucket }));
console.log("Objects:", objects.Contents || []);
console.log("Incomplete uploads:", uploads.Uploads || []);
JS
node --env-file=.env.s3 empty.mjs
Массивы объектов и незавершённых загрузок должны быть пустыми. Если во время экспериментов вы создали multipart-сеанс, используйте операцию прерывания из предыдущего задания с точно указанными принадлежащими вам ключом и идентификатором загрузки, затем повторите эти списки только для чтения. Никогда не игнорируйте молча ошибки запросов списка.
Удалите пустой бакет и его политику
На этом шаге вы удалите принадлежащий вам бакет после успешной проверки объектов и загрузок. Политика является конфигурацией бакета и исчезнет вместе с ним.
npx wrangler r2 bucket delete "$BUCKET" --env-file=.env.management
npx wrangler r2 bucket list --env-file=.env.management
Проверьте точное сгенерированное имя. После успешного выполнения аутентифицированного списка это имя не должно присутствовать в выводе. Выполните проверку удаления на платформе до отзыва токена управления.
Отзовите учётные данные задания и выйдите из системы
На этом шаге вы закроете доступ, оставшийся после выполнения задания. На странице R2 API Tokens отзовите только токен объектов, созданный для этого задания. На странице API Tokens вашего профиля отзовите отдельный токен управления R2, созданный для этого задания. Удаление бакета не отзывает токен, а выход из Wrangler не отзывает учётные данные S3.
После отзыва удалите локальный файл с учётными данными и выйдите из Wrangler на этой виртуальной машине:
rm .env.s3 .env.management
npx wrangler logout
Проверьте структурированную информацию об удостоверении. Ненулевой код завершения при выходе из системы ожидаем:
npx wrangler whoami --json || true
Убедитесь, что loggedIn: false; обычную авторизацию в Dashboard сохраняйте. Платформа проверяет удаление локальных учётных данных и выход из Wrangler. Отзыв обоих токенов выполняется вручную в Dashboard как отдельный контрольный пункт; его нельзя считать выполненным только по удалению файлов.
Резюме
Примените ограниченные областью действия правила истечения срока хранения, будущего перехода между классами хранения и очистки multipart-загрузок, проверьте текущие метаданные, сохраните данные с длительным хранением и выполните явную очистку.



