Skip to content

Commit

Permalink
fix: 兼容平台图片上传-支持远程图片链接
Browse files Browse the repository at this point in the history
  • Loading branch information
terwer committed Aug 13, 2023
1 parent c512e93 commit cf82997
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 58 deletions.
10 changes: 4 additions & 6 deletions src/adaptors/web/zhihu/zhihuWebAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ import * as cheerio from "cheerio"
import { JsonUtil, StrUtil } from "zhi-common"
import { usePicgoBridge } from "~/src/composables/usePicgoBridge.ts"
import { IPublishCfg } from "~/src/types/IPublishCfg.ts"
import { useProxy } from "~/src/composables/useProxy.ts"
import { blobToBuffer } from "~/src/utils/polyfillUtils.ts"
import { base64ToBuffer, remoteImageToBase64Info } from "~/src/utils/polyfillUtils.ts"

/**
* 知乎网页授权适配器
Expand Down Expand Up @@ -104,7 +103,6 @@ class ZhihuWebAdaptor extends BaseWebApi {
const cfg = pubCfg.cfg

const { getImageItemsFromMd } = usePicgoBridge()
const { proxyBlob } = useProxy(cfg.middlewareUrl)

// 找到所有的图片
const images = await getImageItemsFromMd(id, post.markdown)
Expand All @@ -117,9 +115,9 @@ class ZhihuWebAdaptor extends BaseWebApi {

for (const image of images) {
const imageUrl = image.url
const imageBlob = await proxyBlob(imageUrl)
const bits = await blobToBuffer(imageBlob)
const mediaObject = new MediaObject(image.name, imageBlob.type, bits)
const base64Info = await remoteImageToBase64Info(imageUrl)
const bits = base64ToBuffer(base64Info.imageBase64)
const mediaObject = new MediaObject(image.name, base64Info.mimeType, bits)
this.logger.debug("before upload, mediaObject =>", mediaObject)
const attachResult = await this.newMediaObject(mediaObject)
this.logger.debug("attachResult =>", attachResult)
Expand Down
11 changes: 5 additions & 6 deletions src/components/test/CnblogsTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ import { MediaObject, Post } from "zhi-blog-api"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import Adaptors from "~/src/adaptors"
import { useVueI18n } from "~/src/composables/useVueI18n.ts"
import { useProxy } from "~/src/composables/useProxy.ts"
import { blobToBuffer, fileToBuffer, toBuffer } from "~/src/utils/polyfillUtils.ts"
import { base64ToBuffer, remoteImageToBase64 } from "~/src/utils/polyfillUtils.ts"
const logger = createAppLogger("cnblogs-test")
Expand Down Expand Up @@ -327,15 +326,15 @@ const cnblogsHandleApi = async () => {
// 设置文件的元数据
const cnblogsApiAdaptor = await Adaptors.getAdaptor(key)
const cfg = await Adaptors.getCfg(key)
const cnblogsApi = Utils.blogApi(appInstance, cnblogsApiAdaptor)
const { proxyBlob } = useProxy(cfg.middlewareUrl)
logger.info("cnblogsApi=>", cnblogsApi)
const imageUrl = "https://static-rs-terwer.oss-cn-beijing.aliyuncs.com/test/image-20230812091531-hibwr1g.png"
const imageName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1)
const imageBlob = await proxyBlob(imageUrl)
const bits = await blobToBuffer(imageBlob)
const imageBase64 = await remoteImageToBase64(imageUrl)
logger.debug("imageBase64=>", { imageBase64 })
const bits = base64ToBuffer(imageBase64)
logger.debug("bits=>", bits)
const mediaObject = new MediaObject(imageName, "image/png", bits)
logger.info("mediaObject=>", mediaObject)
Expand Down
70 changes: 55 additions & 15 deletions src/components/test/TypechoTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@
import { AppInstance } from "~/src/appInstance.ts"
import { Utils } from "~/src/utils/utils.ts"
import { reactive, ref } from "vue"
import { fileToBuffer } from "~/src/utils/polyfillUtils.ts"
import { SimpleXmlRpcClient } from "simple-xmlrpc"
import { MediaObject } from "zhi-blog-api"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import Adaptors from "~/src/adaptors"
import { base64ToBuffer, remoteImageToBase64 } from "~/src/utils/polyfillUtils.ts"
const logger = createAppLogger("typecho-test")
Expand Down Expand Up @@ -223,23 +222,64 @@ const typechoHandleApi = async () => {
break
}
case METHOD_NEW_MEDIA_OBJECT: {
const file = paramFile.value
const bits = await fileToBuffer(file)
const mediaObject = new MediaObject(file.name, file.type, bits)
const key = "metaweblog_Typecho"
const typechoApiAdaptor = await Adaptors.getAdaptor(key)
const imageUrl = "https://static-rs-terwer.oss-cn-beijing.aliyuncs.com/test/image-20230812091531-hibwr1g.png"
const imageName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1)
const imageBase64 = await remoteImageToBase64(imageUrl)
logger.debug("imageBase64=>", { imageBase64 })
const bits = base64ToBuffer(imageBase64)
logger.debug("bits=>", bits)
const mediaObject = new MediaObject(imageName, "image/png", bits)
logger.info("mediaObject=>", mediaObject)
// 设置文件的元数据
const metadata = {
name: mediaObject.name,
type: mediaObject.type,
bits: mediaObject.bits,
overwrite: true,
}
const xmlrpcApiUrl = "http://127.0.0.1:3000/xmlrpc.php"
const client = new SimpleXmlRpcClient(xmlrpcApiUrl, "", {})
const result = await client.methodCall("metaWeblog.newMediaObject", ["", "terwer", "123456", metadata])
const typechoApi = Utils.blogApi(appInstance, typechoApiAdaptor)
logger.info("typechoApi=>", typechoApi)
const result = await typechoApi.newMediaObject(mediaObject)
logMessage.value = JSON.stringify(result)
logger.info("typecho new mediaObject result=>", result)
// const key = "metaweblog_Typecho"
//
// const file = paramFile.value
// const bits = await fileToBuffer(file)
// const mediaObject = new MediaObject(file.name, file.type, bits)
// logger.info("mediaObject=>", mediaObject)
//
// // 设置文件的元数据
// const metadata = {
// name: mediaObject.name,
// type: mediaObject.type,
// bits: mediaObject.bits,
// overwrite: true,
// }
// const typechoApiAdaptor = await Adaptors.getAdaptor(key)
// const typechoApi = Utils.blogApi(appInstance, typechoApiAdaptor)
// const result = await typechoApi.newMediaObject(metadata)
// logMessage.value = JSON.stringify(result)
// logger.info("typecho new mediaObject result=>", result)
// const file = paramFile.value
// const bits = await fileToBuffer(file)
// const mediaObject = new MediaObject(file.name, file.type, bits)
// logger.info("mediaObject=>", mediaObject)
//
// // 设置文件的元数据
// const metadata = {
// name: mediaObject.name,
// type: mediaObject.type,
// bits: mediaObject.bits,
// overwrite: true,
// }
// const xmlrpcApiUrl = "http://127.0.0.1:3000/xmlrpc.php"
// const client = new SimpleXmlRpcClient(xmlrpcApiUrl, "", {})
// const result = await client.methodCall("metaWeblog.newMediaObject", ["", "terwer", "123456", metadata])
// logMessage.value = JSON.stringify(result)
// logger.info("typecho new mediaObject result=>", result)
break
}
default:
Expand Down
28 changes: 25 additions & 3 deletions src/components/test/WordpressTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ const wordpressHandleApi = async () => {
break
}
case METHOD_NEW_MEDIA_OBJECT: {
const key = "wordpress_Wordpress"
const file = paramFile.value
const bits = await fileToBuffer(file)
const mediaObject = new MediaObject(file.name, file.type, bits)
Expand All @@ -244,11 +246,31 @@ const wordpressHandleApi = async () => {
bits: mediaObject.bits,
overwrite: true,
}
const xmlrpcApiUrl = "http://127.0.0.1:3000/xmlrpc.php"
const client = new SimpleXmlRpcClient(xmlrpcApiUrl, "", {})
const result = await client.methodCall("metaWeblog.newMediaObject", ["", "terwer", "123456", metadata])
const wordpressApiAdaptor = await Adaptors.getAdaptor(key)
const wordpressApi = Utils.blogApi(appInstance, wordpressApiAdaptor)
const result = await wordpressApi.newMediaObject(metadata)
logMessage.value = JSON.stringify(result)
logger.info("wordpress new mediaObject result=>", result)
// const key = "wordpress_Wordpress"
//
// const file = paramFile.value
// const bits = await fileToBuffer(file)
// const mediaObject = new MediaObject(file.name, file.type, bits)
// logger.info("mediaObject=>", mediaObject)
//
// // 设置文件的元数据
// const metadata = {
// name: mediaObject.name,
// type: mediaObject.type,
// bits: mediaObject.bits,
// overwrite: true,
// }
// const cfg = await Adaptors.getCfg(key)
// const client = new SimpleXmlRpcClient(appInstance, cfg.apiUrl, {})
// const result = await client.methodCall("metaWeblog.newMediaObject", ["", cfg.username, cfg.password, metadata])
// logMessage.value = JSON.stringify(result)
// logger.info("wordpress new mediaObject result=>", result)
break
}
default:
Expand Down
16 changes: 1 addition & 15 deletions src/composables/useProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,7 @@ const useProxy = (middlewareUrl?: string) => {
return resJson
}

/**
* 使用代理请求获取 Blob 数据
*
* @param url - 请求的 URL
* @returns 返回请求的 Blob 数据
*/
const proxyBlob = async (url: string): Promise<Blob> => {
const contentType = "image/png"
const response = await proxyFetch(url, [], {}, "GET", contentType)
const ab = await response.arrayBuffer()
logger.debug("arrayBuffer =>", ab)
return new Blob([ab], { type: contentType })
}

return { proxyFetch, proxyXmlrpc, proxyBlob }
return { proxyFetch, proxyXmlrpc }
}

export { useProxy }
15 changes: 13 additions & 2 deletions src/pages/ApiTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,31 @@
-->

<script setup lang="ts">
import { ref } from "vue"
import { onMounted, ref } from "vue"
import { ElTabPane, ElTabs, TabsPaneContext } from "element-plus"
import { useRouter } from "vue-router"
import { useRoute, useRouter } from "vue-router"
import { createAppLogger } from "~/src/utils/appLogger.ts"
const logger = createAppLogger("api-test")
const router = useRouter()
const route = useRoute()
const currentPath = route.path
const activeTab = ref<string>("siyuan")
activeTab.value = currentPath.replace("/test/", "").trim()
const changeTab = (tab: TabsPaneContext, event: Event) => {
const toRoute = `/test/${tab.paneName}`
logger.info(`toRoute=>${toRoute}`)
router.push({ path: toRoute })
}
// onMounted(() => {
// const path = currentPath.replace("/test/", "")
// if (path !== "") {
// activeTab.value = path
// }
// })
</script>

<template>
Expand Down
98 changes: 87 additions & 11 deletions src/utils/polyfillUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/

import { Buffer } from "node:buffer"
import { Base64 } from "js-base64"

/**
* 将 file 对象转换为 Buffer
Expand All @@ -46,18 +47,93 @@ export const fileToBuffer = async (file: any): Promise<any> => {
})
}

export const blobToBuffer = (blob: Blob): Promise<Buffer> => {
/**
* 提取 base64 编码字符串中的 mime 类型。
*
* @param base64Str - 包含 base64 编码数据的字符串
* @returns 提取到的 mime 类型,如果未找到则返回空字符串
*/
const extractMimeType = (base64Str: string): string => {
const match = base64Str.match(/^data:(.*?);base64,/)
if (match && match[1]) {
return match[1]
}
return ""
}

/**
* 将指定URL的图片文件转换为Base64编码字符串
*
* @param imageUrl 图片文件的URL地址
* @returns 图片文件的Base64编码字符串
*/
export const remoteImageToBase64 = async (imageUrl: string): Promise<string> => {
const base64String = await readFileToBase64(imageUrl)
const imageBase64 = base64String.split(";base64,").pop() || ""
return imageBase64
}

/**
* 将远程图片转换为 base64 编码字符串,并获取图片名称和 mime 类型。
*
* @param imageUrl - 包含远程图片URL的字符串
* @returns 包含图片名称、mime 类型和 base64 编码字符串的对象
*/
export const remoteImageToBase64Info = async (
imageUrl: string
): Promise<{ imageName: string; mimeType: string; imageBase64: string }> => {
const base64String = await readFileToBase64(imageUrl)
const imageBase64 = base64String.split(";base64,").pop() || ""
const imageName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1)
const mimeType = extractMimeType(base64String)

return {
imageName,
mimeType,
imageBase64,
}
}

/**
* 将Base64编码的字符串转换为Buffer对象
*
* @param base64Str Base64编码的字符串
* @returns Buffer对象
*/
export const base64ToBuffer = (base64Str: string): Buffer => {
const uintArray = Base64.toUint8Array(base64Str)
const buffer = Buffer.from(uintArray)
return buffer
}

// ================
// private methods
// ================
function readFileToBase64(url: string): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onloadend = () => {
if (reader.readyState === FileReader.DONE) {
const arrayBuffer = reader.result as ArrayBuffer
const buffer = Buffer.from(arrayBuffer)
resolve(buffer)
} else {
reject(new Error("Failed to convert Blob to Buffer."))
;(async () => {
let body = null
try {
const response = await fetch(url)
body = await response.blob()
} catch (e) {
return reject(e)
}
}
reader.readAsArrayBuffer(blob)
if (body != null) {
const reader = new FileReader()
reader.readAsDataURL(body)
reader.onloadend = function () {
var base64data = reader.result as string
resolve(base64data)
}
reader.onerror = function (e) {
reject(e)
}
}
})()
})
}

const uintArrayToBuffer = (uint8Array: Uint8Array): Buffer => {
return Buffer.from(uint8Array)
}

0 comments on commit cf82997

Please sign in to comment.