⚡ Optimize new attachment upload
This commit is contained in:
		@@ -37,7 +37,7 @@
 | 
			
		||||
            color="white"
 | 
			
		||||
            prepend-icon="mdi-launch"
 | 
			
		||||
            text="Open in browser"
 | 
			
		||||
            :href="getAttachmentUrl(item.id)"
 | 
			
		||||
            :href="getAttachmentUrl(item.rid)"
 | 
			
		||||
            target="_blank"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,31 @@
 | 
			
		||||
 | 
			
		||||
    <div class="my-5 w-[640px]">
 | 
			
		||||
      <v-expand-transition>
 | 
			
		||||
        <v-file-input
 | 
			
		||||
          label="File input"
 | 
			
		||||
          variant="solo"
 | 
			
		||||
          :hide-details="true"
 | 
			
		||||
          v-if="!multipartProgress.value"
 | 
			
		||||
          v-model="content"
 | 
			
		||||
        ></v-file-input>
 | 
			
		||||
        <div v-if="!multipartProgress.value">
 | 
			
		||||
          <v-file-input
 | 
			
		||||
            label="File input"
 | 
			
		||||
            variant="solo"
 | 
			
		||||
            :hide-details="true"
 | 
			
		||||
            v-model="content"
 | 
			
		||||
          ></v-file-input>
 | 
			
		||||
 | 
			
		||||
          <v-select
 | 
			
		||||
            label="Storage pool"
 | 
			
		||||
            variant="underlined"
 | 
			
		||||
            :items="poolOptions"
 | 
			
		||||
            item-title="label"
 | 
			
		||||
            item-value="value"
 | 
			
		||||
            density="comfortable"
 | 
			
		||||
            prepend-icon="mdi-database"
 | 
			
		||||
            :hide-details="true"
 | 
			
		||||
            class="mt-5"
 | 
			
		||||
            v-model="pool"
 | 
			
		||||
          >
 | 
			
		||||
            <template v-slot:item="{ props, item }">
 | 
			
		||||
              <v-list-item v-bind="props" :subtitle="item.raw.description" :disabled="item.raw.disabled" />
 | 
			
		||||
            </template>
 | 
			
		||||
          </v-select>
 | 
			
		||||
        </div>
 | 
			
		||||
      </v-expand-transition>
 | 
			
		||||
 | 
			
		||||
      <v-expand-transition>
 | 
			
		||||
@@ -28,9 +46,9 @@
 | 
			
		||||
 | 
			
		||||
      <v-expand-transition>
 | 
			
		||||
        <div v-if="success">
 | 
			
		||||
          <div class="mt-3">
 | 
			
		||||
          <v-card class="mt-3">
 | 
			
		||||
            <attachment-carousel :attachments="[multipartInfo.rid]" />
 | 
			
		||||
          </div>
 | 
			
		||||
          </v-card>
 | 
			
		||||
        </div>
 | 
			
		||||
      </v-expand-transition>
 | 
			
		||||
 | 
			
		||||
@@ -62,7 +80,14 @@ useHead({
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
const poolOptions = [
 | 
			
		||||
  { label: "Interactive", description: "Public indexable, no lifecycle.", value: "interactive" },
 | 
			
		||||
  { label: "Messaging", description: "Has lifecycle, will delete after 14 days.", value: "messaging" },
 | 
			
		||||
  { label: "Dedicated Pool", description: "Your own configuration, coming soon.", value: "dedicated", disabled: true },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
const content = ref<File | null>(null)
 | 
			
		||||
const pool = ref("interactive")
 | 
			
		||||
 | 
			
		||||
const error = ref<string | null>(null)
 | 
			
		||||
const success = ref(false)
 | 
			
		||||
@@ -82,22 +107,30 @@ async function submit() {
 | 
			
		||||
 | 
			
		||||
  loading.value = true
 | 
			
		||||
 | 
			
		||||
  const limit = 3
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    await createMultipartPlaceholder()
 | 
			
		||||
    console.log(`[PAPERCLIP] Multipart placeholder has been created with rid ${multipartInfo.value.rid}`)
 | 
			
		||||
    let taskIdx = 0
 | 
			
		||||
 | 
			
		||||
    multipartProgress.value = 0
 | 
			
		||||
    multipartProgress.current = taskIdx
 | 
			
		||||
    if (multipartInfo.value["file_chunks"]) {
 | 
			
		||||
      multipartProgress.total = Object.keys(multipartInfo.value["file_chunks"] ?? {}).length
 | 
			
		||||
    }
 | 
			
		||||
    for (const chunk in multipartInfo.value["file_chunks"]) {
 | 
			
		||||
    multipartProgress.current = 0
 | 
			
		||||
 | 
			
		||||
    const chunks = Object.keys(multipartInfo.value["file_chunks"] ?? {})
 | 
			
		||||
    multipartProgress.total = chunks.length
 | 
			
		||||
 | 
			
		||||
    const uploadChunks = async (chunk: string) => {
 | 
			
		||||
      await uploadSingleMultipart(chunk)
 | 
			
		||||
      taskIdx++
 | 
			
		||||
      console.log(`[PAPERCLIP] Uploaded multipart ${taskIdx}/${multipartProgress.total}`)
 | 
			
		||||
      multipartProgress.value = taskIdx / multipartProgress.total
 | 
			
		||||
      multipartProgress.current = taskIdx
 | 
			
		||||
      multipartProgress.current++
 | 
			
		||||
      console.log(`[PAPERCLIP] Uploaded multipart ${multipartProgress.current}/${multipartProgress.total}`)
 | 
			
		||||
      multipartProgress.value = multipartProgress.current / multipartProgress.total
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < chunks.length; i += limit) {
 | 
			
		||||
      const chunkSlice = chunks.slice(i, i + limit)
 | 
			
		||||
      await Promise.all(chunkSlice.map(uploadChunks))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (multipartInfo.value["is_uploaded"]) {
 | 
			
		||||
      console.log(`[PAPERCLIP] Entire file has been uploaded in ${multipartProgress.total} chunk(s)`)
 | 
			
		||||
      success.value = true
 | 
			
		||||
@@ -113,6 +146,12 @@ async function submit() {
 | 
			
		||||
async function createMultipartPlaceholder() {
 | 
			
		||||
  if (!content.value) return
 | 
			
		||||
 | 
			
		||||
  const mimetypeMap: { [id: string]: string } = {
 | 
			
		||||
    "mp4": "video/mp4",
 | 
			
		||||
    "mov": "video/quicktime",
 | 
			
		||||
  }
 | 
			
		||||
  const mimetype = mimetypeMap[content.value.name.split(".").pop() as string]
 | 
			
		||||
 | 
			
		||||
  const nameArray = content.value.name.split(".")
 | 
			
		||||
  nameArray.pop()
 | 
			
		||||
 | 
			
		||||
@@ -120,10 +159,11 @@ async function createMultipartPlaceholder() {
 | 
			
		||||
    method: "POST",
 | 
			
		||||
    headers: { "Content-Type": "application/json" },
 | 
			
		||||
    body: JSON.stringify({
 | 
			
		||||
      pool: "interactive",
 | 
			
		||||
      pool: pool.value,
 | 
			
		||||
      size: content.value.size,
 | 
			
		||||
      name: content.value.name,
 | 
			
		||||
      alt: nameArray.join("."),
 | 
			
		||||
      mimetype: mimetype,
 | 
			
		||||
    }),
 | 
			
		||||
  })
 | 
			
		||||
  if (resp.status != 200) throw new Error(await resp.text())
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user