"use client"; import baseRequest from "./baseRequest"; import CryptoJS from "crypto-js"; import { Toast } from "antd-mobile"; import { generateSignature } from "@/utils/crypto"; import { generateVideoThumbnails } from "@rajesh896/video-thumbnails-generator"; //获取auth async function getAuth(mtype) { const base = baseRequest(); const signature = generateSignature({ mtype: mtype, ...base, }); try { const response = await fetch(`/api/media/auth?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ mtype: mtype, ...base, }), }); const data = await response.json(); if (data.ret === -1) { Toast.show({ content: data.msg, }); return; } return data.data.policy_token; } catch (error) { // console.error(error); } } //计算媒体参数 async function calculateFileMetadata(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (event) => { try { const fileData = event.target.result; const wordArray = CryptoJS.lib.WordArray.create(fileData); const md5Hash = CryptoJS.MD5(wordArray).toString(); if (typeof window == "undefined") return; const mediaElement = document.createElement( file.type.indexOf("image/") != -1 ? "img" : "video" ); mediaElement.src = URL.createObjectURL(file); mediaElement.addEventListener( file.type.indexOf("image/") != -1 ? "load" : "loadedmetadata", () => { const metadata = { md5Hash, width: mediaElement.width || 0, height: mediaElement.height || 0, duration: mediaElement.duration || 0, }; resolve(metadata); } ); mediaElement.addEventListener("error", () => { const metadata = { md5Hash, width: 0, height: 0, duration: 0, }; resolve(metadata); }); } catch (error) { reject(error); } }; reader.onerror = (error) => { reject(error); }; file.type?.indexOf("image/") != -1 ? reader.readAsDataURL(file) : reader.readAsArrayBuffer(file); }); } //计算视频封面参数 async function calculateCoverMetadata(file) { return new Promise((resolve, reject) => { try { const pureBase64String = file.replace( /^data:image\/(png|jpg|jpeg|gif);base64,/, "" ); const binaryString = atob(pureBase64String); const binaryData = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { binaryData[i] = binaryString.charCodeAt(i); } const wordArray = CryptoJS.lib.WordArray.create(binaryData); const md5Hash = CryptoJS.MD5(wordArray).toString(); const image = new Image(); image.src = `data:image/jpeg;base64,${pureBase64String}`; image.onload = () => { const metadata = { md5Hash, width: image.width || 0, height: image.height || 0, duration: image.duration || 0, }; resolve(metadata); }; image.onerror = () => { const metadata = { md5Hash, width: 0, height: 0, }; resolve(metadata); }; } catch (error) { reject(error); } }); } function atob(base64) { var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, dec = "", tmp_arr = []; if (!base64) { return base64; } base64 += ""; do { // unpack four hexets into three bytes using index points in b64 h1 = b64.indexOf(base64.charAt(i++)); h2 = b64.indexOf(base64.charAt(i++)); h3 = b64.indexOf(base64.charAt(i++)); h4 = b64.indexOf(base64.charAt(i++)); bits = (h1 << 18) | (h2 << 12) | (h3 << 6) | h4; o1 = (bits >> 16) & 0xff; o2 = (bits >> 8) & 0xff; o3 = bits & 0xff; if (h3 == 64) { tmp_arr[ac++] = String.fromCharCode(o1); } else if (h4 == 64) { tmp_arr[ac++] = String.fromCharCode(o1, o2); } else { tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); } } while (i < base64.length); dec = tmp_arr.join(""); return dec; } //获取上传失败时返回的id async function getFailId() { const base = await baseRequest(); const signature = await generateSignature({ ...base, }); try { const response = await fetch( `/api/upload_media_fail_config/list?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ ...base, }), } ); const data = await response.json(); if (data.ret === -1) { Toast.show({ icon: "fail", content: data.msg, position: "top", }); return; } return data.data; } catch (e) { // console.warn(e); } } //上传单张图片 export async function uploadImage(asset, h, w) { const auth = await getAuth(1); try { const formData = new FormData(); formData.append("name", auth.filename); formData.append("policy", auth.policy); formData.append("OSSAccessKeyId", auth.access_key_id); formData.append("success_action_status", "200"); formData.append("signature", auth.signature); formData.append("key", auth.directory + "/" + auth.filename); formData.append("file", asset); const uploadResponse = await fetch(auth.host, { method: "POST", body: formData, }); if (uploadResponse.status === 200) { const info = await calculateFileMetadata(asset); const item = { src_id: auth.directory + "/" + auth.filename, md5: info.md5Hash, h: h || info.height, w: h || info.height, fmt: asset.type, }; const base = baseRequest(); const response = await fetch(`/api/media/c_upload`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ mtype: 1, item: item, ...base, }), }); const data = await response.json(); if (data.ret === -1) { Toast.show({ content: data.msg, }); return; } return data.data.ret_item.id; } else { Toast.show({ content: "上传图片失败", }); } } catch (error) { // console.log("Error occurred while getting or uploading data:", error); } } //上传视频封面 export async function uploadVideoCover(asset) { const auth = await getAuth(1); function base64toFile(base64Data) { //去掉base64的头部信息,并转换为byte let split = base64Data.split(","); let bytes = window.atob(split[1]); //获取文件类型 let fileType = split[0].match(/:(.*?);/)[1]; //处理异常,将ascii码小于0的转换为大于0 let ab = new ArrayBuffer(bytes.length); let ia = new Uint8Array(ab); for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new Blob([ab], { type: fileType }); } const cover = await generateVideoThumbnails(asset, 1); const blobFile = base64toFile(cover[0]); try { const formData = new FormData(); formData.append("name", auth.filename); formData.append("policy", auth.policy); formData.append("OSSAccessKeyId", auth.access_key_id); formData.append("success_action_status", "200"); formData.append("signature", auth.signature); formData.append("key", auth.directory + "/" + auth.filename); formData.append("file", blobFile); const uploadResponse = await fetch(auth.host, { method: "POST", body: formData, }); if (uploadResponse.status === 200) { const info = await calculateCoverMetadata(cover[0]); const item = { src_id: auth.directory + "/" + auth.filename, md5: info.md5Hash, h: info.height, w: info.width, fmt: blobFile.type, }; const base = baseRequest(); const response = await fetch("/api/media/c_upload", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ mtype: 1, item: item, ...base, }), }); const data = await response.json(); if (data.ret === -1) { alert(data.msg); return; } return data.data.ret_item.id; } else { alert("上传图片失败"); } } catch (error) { // console.log("Error occurred while getting or uploading data:", error); } } //上传单个视频 export async function uploadVideo(asset) { const auth = await getAuth(2); try { const formData = new FormData(); formData.append("name", auth.filename); formData.append("policy", auth.policy); formData.append("OSSAccessKeyId", auth.access_key_id); formData.append("success_action_status", "200"); formData.append("signature", auth.signature); formData.append("key", auth.directory + "/" + auth.filename); formData.append("file", asset); const uploadResponse = await fetch(auth.host, { method: "POST", body: formData, }); if (uploadResponse.status === 200) { const videoCoverId = await uploadVideoCover(asset); const info = await calculateFileMetadata(asset); const item = { src_id: auth.directory + "/" + auth.filename, cover_id: videoCoverId, md5: info.md5Hash, fmt: asset?.type, dur: info?.duration, }; const base = baseRequest(); const response = await fetch(`/api/media/c_upload`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ mtype: 2, item: item, ...base, }), }); const data = await response.json(); if (data.ret === -1) { Toast.show({ icon: "fail", content: data.msg, position: "top", }); return; } return data.data.ret_item.id; } else { Toast.show({ icon: "fail", content: "上传视频失败", position: "top", }); const failId = await getFailId(); return failId?.video_id_for_upload_fail; } } catch (error) { // console.log("Error occurred while getting or uploading data:", error); Toast.show({ icon: "fail", content: "上传视频失败", position: "top", }); const failId = await getFailId(); return failId?.video_id_for_upload_fail; } } //上传多个图片 export async function multiUploadImage(assets, type) { let ids = { image_ids: [], video_ids: [] }; await Promise.all( assets.map(async (asset) => { if (type == 1) { const id = await uploadImage(asset); ids.image_ids.push(id); } else { const id = await uploadVideo(asset); id && ids.video_ids.push(id); } }) ); return ids; }