Skip to content

Commit

Permalink
fix: 修复上传文件时没有指定文件名会导致 mime 错误的问题
Browse files Browse the repository at this point in the history
  • Loading branch information
rxliuli committed Oct 22, 2022
1 parent 9c1051c commit ff900b8
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 52 deletions.
3 changes: 3 additions & 0 deletions apps/joplin-vscode-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@
"@types/koa__cors": "^3.3.0",
"@types/koa__router": "^12.0.0",
"@types/markdown-it": "^12.2.3",
"@types/mime-types": "^2.1.1",
"@types/node": "^16",
"@types/vscode": "^1.71.0",
"chokidar": "^3.5.3",
"cross-path-sort": "^1.0.0",
"envfile": "^6.18.0",
"fetch-blob": "^3.2.0",
"formdata-polyfill": "^4.0.10",
"joplin-api": "workspace:*",
"koa": "^2.13.4",
"markdown-it": "^13.0.1",
"mime-types": "^2.1.35",
"node-fetch": "^3.2.10",
"node-html-parser": "^6.1.1",
"tsup": "^6.2.3",
Expand Down
20 changes: 13 additions & 7 deletions apps/joplin-vscode-plugin/src/service/JoplinNoteCommandService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { FolderOrNoteExtendsApi } from '../api/FolderOrNoteExtendsApi'
import { appConfig, AppConfig } from '../config/AppConfig'
import { JoplinNoteUtil } from '../util/JoplinNoteUtil'
import * as path from 'path'
import { close, createReadStream, mkdirp, pathExists, readFile, remove, writeFile } from '@liuli-util/fs-extra'
import { close, mkdirp, pathExists, readFile, remove, writeFile } from '@liuli-util/fs-extra'
import { createEmptyFile } from '../util/createEmptyFile'
import { UploadResourceUtil } from '../util/UploadResourceUtil'
import { uploadResourceService } from './UploadResourceService'
Expand All @@ -34,7 +34,6 @@ import { filenamify } from '../util/filenamify'
import { NoteProperties } from 'joplin-api'
import { logger } from '../constants/logger'
import { loadLastNoteList } from '../util/api'
import { Blob } from 'buffer'

export class JoplinNoteCommandService {
private folderOrNoteExtendsApi = new FolderOrNoteExtendsApi()
Expand Down Expand Up @@ -89,11 +88,18 @@ export class JoplinNoteCommandService {
if (!id) {
return
}
await resourceApi.update({
id,
// title: path.basename(filePath),
data: new Blob([await readFile(filePath)]),
})
try {
const data = await readFile(filePath)
const r = await resourceApi.update({
id,
// @ts-expect-error
data: new Blob([data]),
filename: path.basename(filePath),
})
console.log('resource update? ', r)
} catch (err) {
logger.error('update resource error: ' + err)
}
})
.on('error', (err) => {
logger.error('watch resource error: ' + err)
Expand Down
28 changes: 9 additions & 19 deletions apps/joplin-vscode-plugin/src/util/UploadResourceUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { existsSync, mkdirpSync, readFile } from '@liuli-util/fs-extra'
import { spawn } from 'child_process'
import { resourceApi } from 'joplin-api'
import { RootPath } from '../RootPath'
import { Blob } from 'buffer'

/**
* for clipboard image
Expand All @@ -16,29 +15,20 @@ export interface IClipboardImage {

export class UploadResourceUtil {
static async uploadByPath(filePath: string, isImage: boolean) {
const param = {
title: path.basename(filePath),
const title = path.basename(filePath)
console.log('uploadFromExplorer begin: ', filePath, title)
const res = await resourceApi.create({
title,
// @ts-expect-error
data: new Blob([await readFile(filePath)]),
}
console.log('uploadFromExplorer begin: ', filePath, param.title)
const res = await resourceApi.create(param)
const markdownLink = `${isImage ? '!' : ''}[${param.title}](:/${res.id})`
filename: title,
})
console.log('uploadByPath', res)
const markdownLink = `${isImage ? '!' : ''}[${title}](:/${res.id})`
console.log('uploadFromExplorer end: ', markdownLink)
return { res, markdownLink }
}

static async uploadFileByPath(filePath: string) {
const param = {
title: path.basename(filePath),
data: new Blob([await readFile(filePath)]),
}
console.log('uploadFileFromExplorer begin: ', filePath, param.title)
const res = await resourceApi.create(param)
const markdownLink = `[${res.title}](:/${res.id})`
console.log('uploadFileFromExplorer end: ', markdownLink)
return { res, markdownLink }
}

static getCurrentPlatform(): string {
const platform = process.platform
if (platform !== 'win32') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { close, mkdirp, pathExists, readFile, remove } from '@liuli-util/fs-extra'
import { config, resourceApi } from 'joplin-api'
import path from 'path'
import { beforeEach, expect, it } from 'vitest'
import { findParent } from '../findParent'
import { UploadResourceUtil } from '../UploadResourceUtil'
import { parse } from 'envfile'
import { createEmptyFile } from '../createEmptyFile'
import '../node-polyfill'

const tempPath = path.resolve(__dirname, '.temp', path.basename(__filename))
beforeEach(async () => {
await remove(tempPath)
await mkdirp(tempPath)
const dirPath = await findParent(__dirname, (item) => pathExists(path.resolve(item, 'package.json')))
const envPath = path.resolve(dirPath!, '.env.local')
if (!(await pathExists(envPath))) {
throw new Error('请更新 .env.local 文件:' + envPath)
}
const env = await readFile(envPath, 'utf8')

config.token = parse(env).TOKEN!
config.baseUrl = 'http://127.0.0.1:27583'
})

it('test create by empty file', async () => {
const fsPath = path.resolve(__dirname, 'assets/test.km.svg')
const { res, markdownLink } = await UploadResourceUtil.uploadByPath(fsPath, true)
expect(res.mime).eq('image/svg+xml')
expect(markdownLink).eq(`![${path.basename(fsPath)}](:/${res.id})`)
const data = await readFile(fsPath)
const buffer = await resourceApi.fileByResourceId(res.id)
expect(data).deep.eq(buffer)
console.log(data.length)
})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions apps/joplin-vscode-plugin/src/util/findParent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import path from 'path'

/**
* 向上查找目录
* @param cwd
* @param predicate
*/
export function findParent(cwd: string, predicate: (dir: string) => boolean): string | null
export function findParent(cwd: string, predicate: (dir: string) => Promise<boolean>): Promise<string | null>
export function findParent<T extends (dir: string) => boolean | Promise<boolean>, R extends string | null>(
cwd: string,
predicate: T,
): ReturnType<T> extends Promise<any> ? Promise<R> : R {
const res = predicate(cwd)
function f(res: boolean): string | null {
if (res) {
return cwd
}
const parent = path.dirname(cwd)
if (parent === cwd) {
return null
}
return findParent(parent, predicate as any)
}

return res instanceof Promise ? res.then(f) : (f(res) as any)
}
13 changes: 5 additions & 8 deletions apps/joplin-vscode-plugin/src/util/node-polyfill.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import nodeFetch from 'node-fetch'
import fetch from 'node-fetch'
import { FormData } from 'formdata-polyfill/esm.min.js'
import { Blob } from 'fetch-blob'

// @ts-expect-errors
if (typeof fetch === 'undefined') {
Reflect.set(globalThis, 'fetch', nodeFetch)
}
if (typeof FormData === 'undefined') {
Reflect.set(globalThis, 'FormData', FormData)
}
Reflect.set(globalThis, 'fetch', fetch)
Reflect.set(globalThis, 'FormData', FormData)
Reflect.set(globalThis, 'Blob', Blob)
4 changes: 4 additions & 0 deletions libs/joplin-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@
"devDependencies": {
"@liuli-util/eslint-config-ts": "^0.4.0",
"@types/fs-extra": "^9.0.13",
"@types/node": "^18.11.3",
"envfile": "^6.18.0",
"fetch-blob": "^3.2.0",
"formdata-polyfill": "^4.0.10",
"fs-extra": "^10.1.0",
"node-fetch": "^3.2.10",
"rimraf": "^3.0.2",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",
Expand Down
8 changes: 5 additions & 3 deletions libs/joplin-api/src/api/ResourceApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ export class ResourceApi {
* The "data" field is required, while the "props" one is not. If not specified, default values will be used.
* @param param
*/
async create(param: { data: Blob | BufferBlob } & Partial<ResourceProperties>): Promise<ResourceGetRes> {
async create(param: { data: Blob | BufferBlob } & Partial<ResourceProperties>): Promise<ResourceProperties> {
const { data, ...others } = param
return (await this.ajax.postFormData('/resources', 'post', {
props: JSON.stringify(others),
data: param.data,
})) as ResourceGetRes
data: data,
filename: param.filename,
})) as ResourceProperties
}

async update(
Expand All @@ -57,6 +58,7 @@ export class ResourceApi {
return await this.ajax.postFormData<ResourceGetRes>(`/resources/${id}`, 'put', {
props: JSON.stringify(others),
data: data,
filename: param.filename,
})
}

Expand Down
25 changes: 21 additions & 4 deletions libs/joplin-api/src/api/__tests__/ResourceApi.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, it, describe, beforeAll, afterAll, beforeEach } from 'vitest'
import { resourceApi } from '../..'
import { mkdirp, pathExists, readFile, remove, stat, writeFile } from 'fs-extra'
import { mkdirp, pathExists, readFile, remove, stat, writeFile, open, close } from 'fs-extra'
import { createTestResource } from './utils/CreateTestResource'
import path from 'path'
import { setupTestEnv } from '../../util/setupTestEnv'
Expand Down Expand Up @@ -40,12 +40,29 @@ it.skip('test get filename', async () => {
const resourcePath = path.resolve(__dirname, './assets/resourcesByFileId.png')
it('test create', async () => {
const title = 'image title'
const json = await resourceApi.create({
const r = await resourceApi.create({
title,
data: new Blob([await readFile(resourcePath)]),
})
console.log(json.id)
expect(json.title).toBe(title)
console.log(r)
expect(r.title).toBe(title)
})
it.only('test create empty file', async () => {
const fsPath = path.resolve(tempPath, 'test.km.svg')
const handle = await open(fsPath, 'w')
try {
expect(await pathExists(fsPath)).toBeTruthy()
const r = await resourceApi.create({
title: path.basename(fsPath),
data: new Blob([await readFile(fsPath)]),
filename: path.basename(fsPath),
})
console.log(r)
expect(r.mime).eq('image/svg+xml')
expect(r.file_extension).eq('svg')
} finally {
await close(handle)
}
})
it('create by buffer', async () => {
const title = 'image title'
Expand Down
1 change: 1 addition & 0 deletions libs/joplin-api/src/api/__tests__/assets/test.km.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { mkdirp, pathExists, readFile, remove, open, close, writeFile } from 'fs-extra'
import path from 'path'
import { beforeEach, expect, it } from 'vitest'
import { resourceApi } from '../../JoplinApiGenerator'
import fetch from 'node-fetch'
import { FormData } from 'formdata-polyfill/esm.min.js'
import { Blob } from 'fetch-blob'

const tempPath = path.resolve(__dirname, '.temp')
beforeEach(async () => {
await remove(tempPath)
await mkdirp(tempPath)
Reflect.set(globalThis, 'fetch', fetch)
Reflect.set(globalThis, 'FormData', FormData)
Reflect.set(globalThis, 'Blob', Blob)
})

it('create empty file and polyfill', async () => {
const emptyPath = path.resolve(tempPath, 'test.km.svg')
const handle = await open(emptyPath, 'w')
try {
expect(await pathExists(emptyPath)).toBeTruthy()
const r = await resourceApi.create({
title: path.basename(emptyPath),
data: new Blob([await readFile(emptyPath)]),
})
expect(r).not.undefined
} finally {
await close(handle)
}
})

it('create file and polyfill', async () => {
const fsPath = path.resolve(__dirname, '../assets/resourcesByFileId.png')
const data = await readFile(fsPath)
const r = await resourceApi.create({
title: path.basename(fsPath),
data: new Blob([data]),
})
expect(r).not.undefined
expect(data).deep.eq(await resourceApi.fileByResourceId(r.id))
})

it('update file', async () => {
const emptyPath = path.resolve(tempPath, 'test.km.svg')
const fsPath = path.resolve(__dirname, '../assets/resourcesByFileId.png')
const handle = await open(emptyPath, 'w')
try {
expect(await pathExists(emptyPath)).toBeTruthy()
const r = await resourceApi.create({
title: path.basename(emptyPath),
data: new Blob([await readFile(emptyPath)]),
})
expect(r).not.undefined
const data = await readFile(fsPath)
await resourceApi.update({ id: r.id, data: new Blob([data]) })
expect(data).deep.eq(await resourceApi.fileByResourceId(r.id))
} finally {
await close(handle)
}
})
19 changes: 13 additions & 6 deletions libs/joplin-api/src/util/ajax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,20 @@ export class Ajax {
})
}

async postFormData<T>(url: string, method: 'post' | 'put', data: object): Promise<T> {
async postFormData<T>(
url: string,
method: 'post' | 'put',
data: {
props: string
data?: Blob
filename?: string
},
): Promise<T> {
const fd = new FormData()
Object.entries(data).forEach(([k, v]) => {
if (k && v) {
fd.append(k, v)
}
})
fd.append('props', data.props)
if (data.data) {
fd.append('data', data.data, data.filename)
}
return await this.request({ url: this.baseUrl(url), method: method, data: fd })
}
}
Loading

0 comments on commit ff900b8

Please sign in to comment.