mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
+21
-8
@@ -22,7 +22,8 @@
|
||||
// whenever S3_ENDPOINT isn't reachable from this machine —
|
||||
// see resolvePublicHost() below)
|
||||
|
||||
const { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadBucketCommand } = require("@aws-sdk/client-s3");
|
||||
const { S3Client, DeleteObjectCommand, GetObjectCommand, HeadBucketCommand } = require("@aws-sdk/client-s3");
|
||||
const { Upload } = require("@aws-sdk/lib-storage");
|
||||
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
||||
const crypto = require("crypto");
|
||||
const path = require("path");
|
||||
@@ -127,7 +128,13 @@ async function buildPublicUrl(key, bucket = DEFAULT_BUCKET) {
|
||||
// input: { buffer, originalname, mimetype, ownerType? }
|
||||
// output: { url, uuid } ← uuid = the S3 key, stored as storage_key in DB
|
||||
//
|
||||
async function uploadFile({ buffer, originalname, mimetype, ownerType = "image" }) {
|
||||
// onProgress?: ({ loaded, total }) => void — real bytes-sent-to-Garage events,
|
||||
// straight from the AWS SDK's own httpUploadProgress, not simulated. A plain
|
||||
// PutObjectCommand (what this used to be) has no progress API at all; Upload
|
||||
// auto-splits into multipart above its ~5MB partSize threshold, so large
|
||||
// files (the case this actually matters for) report genuine incremental
|
||||
// progress per part, while small ones just jump from 0 to 100 immediately.
|
||||
async function uploadFile({ buffer, originalname, mimetype, ownerType = "image", onProgress }) {
|
||||
if (!buffer) {
|
||||
throw Object.assign(new Error("File buffer is required for S3 uploads."), { status: 400 });
|
||||
}
|
||||
@@ -135,12 +142,18 @@ async function uploadFile({ buffer, originalname, mimetype, ownerType = "image"
|
||||
const bucket = DEFAULT_BUCKET;
|
||||
const key = buildKey(originalname, ownerType);
|
||||
|
||||
await s3.send(new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
Body: buffer,
|
||||
ContentType: mimetype,
|
||||
}));
|
||||
const uploader = new Upload({
|
||||
client: s3,
|
||||
params: { Bucket: bucket, Key: key, Body: buffer, ContentType: mimetype },
|
||||
});
|
||||
|
||||
if (onProgress) {
|
||||
uploader.on("httpUploadProgress", (progress) => {
|
||||
onProgress({ loaded: progress.loaded ?? 0, total: progress.total ?? buffer.length });
|
||||
});
|
||||
}
|
||||
|
||||
await uploader.done();
|
||||
|
||||
return {
|
||||
url: await buildPublicUrl(key, bucket),
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name: uploadProgress.service.js
|
||||
* Type of Program: Service
|
||||
* Description: In-memory SSE broadcaster for real upload progress on the
|
||||
* Express -> S3 (Garage) leg of an asset upload. Keyed by a
|
||||
* client-generated uploadId so the browser can open the stream
|
||||
* before the upload request itself is even sent.
|
||||
*
|
||||
* No Redis — single-process only, same tradeoff already made by
|
||||
* mediaToken.service.js's in-memory token cache. Fine for one
|
||||
* instance; a second app instance would just never see progress
|
||||
* for uploads routed to the other process.
|
||||
*
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
***********************************************************************************************************************************************************************/
|
||||
"use strict";
|
||||
|
||||
const clients = new Map(); // uploadId -> Response
|
||||
|
||||
function subscribe(uploadId, res) {
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
Connection: "keep-alive",
|
||||
"X-Accel-Buffering": "no", // disable proxy-side buffering (nginx-style intermediaries)
|
||||
});
|
||||
res.write(": connected\n\n");
|
||||
clients.set(uploadId, res);
|
||||
|
||||
res.on("close", () => {
|
||||
if (clients.get(uploadId) === res) clients.delete(uploadId);
|
||||
});
|
||||
}
|
||||
|
||||
// No-ops if nobody's subscribed (client never opened the stream, or already
|
||||
// disconnected) — progress is a best-effort visual, never load-bearing for
|
||||
// the actual upload.
|
||||
function publish(uploadId, data) {
|
||||
const res = clients.get(uploadId);
|
||||
if (!res) return;
|
||||
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
||||
}
|
||||
|
||||
function complete(uploadId, data = {}) {
|
||||
const res = clients.get(uploadId);
|
||||
if (!res) return;
|
||||
res.write(`data: ${JSON.stringify({ ...data, done: true })}\n\n`);
|
||||
res.end();
|
||||
clients.delete(uploadId);
|
||||
}
|
||||
|
||||
module.exports = { subscribe, publish, complete };
|
||||
Reference in New Issue
Block a user