Skip to content

Commit

Permalink
feat(utils): 新增 parseDataUrl
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Nov 27, 2020
1 parent a7d02bd commit b67d872
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 4 deletions.
193 changes: 193 additions & 0 deletions src/utils/__snapshots__/parseDataUrl.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`parseDataUrl 表现正常 1`] = `
Object {
"base64": false,
"content": "some-data",
"mimeType": "image/svg+xml;name=foobar (1).svg;charset=UTF-8",
"parameters": Object {
"charset": "UTF-8",
"name": "foobar (1).svg",
},
}
`;

exports[`parseDataUrl 表现正常 2`] = `
Object {
"base64": true,
"content": "PGh0bWw+PC9odG1sPg==",
"mimeType": "application/vnd.ms-excel",
"parameters": Object {
"base64": "",
},
}
`;

exports[`parseDataUrl 表现正常 3`] = `
Object {
"base64": true,
"content": <h1>
Hello!
</h1>,
"mimeType": "video/x-ms-wmv",
"parameters": Object {
"base64": "",
},
}
`;

exports[`parseDataUrl 表现正常 4`] = `
Object {
"base64": true,
"content": <h1>
Hello!
</h1>,
"mimeType": "audio/mp3",
"parameters": Object {
"base64": "",
},
}
`;

exports[`parseDataUrl 表现正常 5`] = `
Object {
"base64": false,
"content": <h1>
Hello!
</h1>,
"mimeType": "text/html;charset=US-ASCII",
"parameters": Object {
"charset": "US-ASCII",
},
}
`;

exports[`parseDataUrl 表现正常 6`] = `
Object {
"base64": false,
"content": "A brief note",
"mimeType": "text/plain;charset=US-ASCII",
"parameters": Object {
"charset": "US-ASCII",
},
}
`;

exports[`parseDataUrl 表现正常 7`] = `
Object {
"base64": false,
"content": <h1>
Hello, World!
</h1>,
"mimeType": "text/html",
"parameters": Object {},
}
`;

exports[`parseDataUrl 表现正常 8`] = `
Object {
"base64": true,
"content": "SGVsbG8sIFdvcmxkIQ==",
"mimeType": "text/plain",
"parameters": Object {
"base64": "",
},
}
`;

exports[`parseDataUrl 表现正常 9`] = `
Object {
"base64": false,
"content": "Hello World!",
"mimeType": "text/plain;charset=US-ASCII",
"parameters": Object {
"charset": "US-ASCII",
},
}
`;

exports[`parseDataUrl 表现正常 10`] = `
Object {
"base64": false,
"content": "Hello, World!",
"mimeType": "text/plain;charset=US-ASCII",
"parameters": Object {
"charset": "US-ASCII",
},
}
`;

exports[`parseDataUrl 表现正常 11`] = `
Object {
"base64": true,
"content": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCBmaWxsPSIjMDBCMUZGIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIvPjwvc3ZnPg==",
"mimeType": "image/svg+xml",
"parameters": Object {
"base64": "",
},
}
`;

exports[`parseDataUrl 表现正常 12`] = `
Object {
"base64": true,
"content": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC",
"mimeType": "image/png;name=foo.png",
"parameters": Object {
"base64": "",
"name": "foo.png",
},
}
`;

exports[`parseDataUrl 表现正常 13`] = `
Object {
"base64": false,
"content": <svg xmlns="http://www.w3.org/2000/svg"
width="100"
height="100"
>
<rect fill="#00B1FF"
width="100"
height="100"
>
</rect>
</svg>,
"mimeType": "image/svg+xml;charset=utf-8;name=bar.svg",
"parameters": Object {
"charset": "utf-8",
"name": "bar.svg",
},
}
`;

exports[`parseDataUrl 表现正常 14`] = `
Object {
"base64": false,
"content": <svg xmlns="http://www.w3.org/2000/svg"
width="100"
height="100"
>
<rect fill="#00B1FF"
width="100"
height="100"
>
</rect>
</svg>,
"mimeType": "image/svg+xml;charset=utf-8",
"parameters": Object {
"charset": "utf-8",
},
}
`;

exports[`parseDataUrl 表现正常 15`] = `
Object {
"base64": true,
"content": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC",
"mimeType": "image/png",
"parameters": Object {
"base64": "",
},
}
`;
1 change: 1 addition & 0 deletions src/utils/createUrlQueryString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface CreateUrlQueryStringOptions {
* 创建 url 查询字符串。
*
* @param parameters 查询参数
* @param options 选项
* @returns 返回 url 查询字符串
* @example
* ```typescript
Expand Down
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export * from './md5'
export * from './move'
export * from './omitStrict'
export * from './onceMeanwhile'
export * from './parseDataUrl'
export * from './parseUrlQueryString'
export * from './pascalCase'
export * from './pickStrict'
Expand Down
2 changes: 1 addition & 1 deletion src/utils/isDataUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ export function isDataUrl(value: string) {
return isDataUrl.regex.test(value)
}

isDataUrl.regex = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)$/i
isDataUrl.regex = /^data:(([a-z]+\/[a-z0-9-+.]+)(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)$/i
59 changes: 59 additions & 0 deletions src/utils/parseDataUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { parseDataUrl } from './parseDataUrl'

describe('parseDataUrl', () => {
test('表现正常', () => {
expect(
parseDataUrl(
'data:image/svg+xml;name=foobar%20(1).svg;charset=UTF-8,some-data',
),
).toMatchSnapshot()
expect(
parseDataUrl(
'data:application/vnd.ms-excel;base64,PGh0bWw%2BPC9odG1sPg%3D%3D',
),
).toMatchSnapshot()
expect(
parseDataUrl('data:video/x-ms-wmv;base64,%3Ch1%3EHello!%3C%2Fh1%3E'),
).toMatchSnapshot()
expect(
parseDataUrl('data:audio/mp3;base64,%3Ch1%3EHello!%3C%2Fh1%3E'),
).toMatchSnapshot()
expect(
parseDataUrl('data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E'),
).toMatchSnapshot()
expect(parseDataUrl('data:,A%20brief%20note')).toMatchSnapshot()
expect(
parseDataUrl('data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'),
).toMatchSnapshot()
expect(
parseDataUrl('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D'),
).toMatchSnapshot()
expect(parseDataUrl('data:,Hello World!')).toMatchSnapshot()
expect(parseDataUrl('data:,Hello%2C%20World!')).toMatchSnapshot()
expect(
parseDataUrl(
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCBmaWxsPSIjMDBCMUZGIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIvPjwvc3ZnPg==',
),
).toMatchSnapshot()
expect(
parseDataUrl(
'data:image/png;name=foo.png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC',
),
).toMatchSnapshot()
expect(
parseDataUrl(
'data:image/svg+xml;charset=utf-8;name=bar.svg,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20fill%3D%22%2300B1FF%22%20width%3D%22100%22%20height%3D%22100%22%2F%3E%3C%2Fsvg%3E',
),
).toMatchSnapshot()
expect(
parseDataUrl(
'data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20fill%3D%22%2300B1FF%22%20width%3D%22100%22%20height%3D%22100%22%2F%3E%3C%2Fsvg%3E',
),
).toMatchSnapshot()
expect(
parseDataUrl(
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC',
),
).toMatchSnapshot()
})
})
44 changes: 44 additions & 0 deletions src/utils/parseDataUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { isDataUrl } from './isDataUrl'
import { LiteralUnion } from '../types'
import { parseUrlQueryString } from './parseUrlQueryString'

export interface ParseDataUrlResult {
mimeType: string
parameters: Record<
LiteralUnion<'name' | 'charset' | 'base64', string>,
string
>
content: string
base64: boolean
}

/**
* 解析 Data URL。
*
* @param dataUrl 要解析的 Data URL
* @returns 返回结果
*/
export function parseDataUrl(dataUrl: string): ParseDataUrlResult {
const [
,
mimeType = 'text/plain;charset=US-ASCII',
,
,
extraParameters = '',
content = '',
] = dataUrl.match(isDataUrl.regex) || []

const parameters = parseUrlQueryString(
`${mimeType}${extraParameters}`.replace(/^[^;]*;?/, ''),
{
partSeparator: ';',
},
)

return {
mimeType: decodeURIComponent(mimeType),
parameters: parameters,
content: decodeURIComponent(content),
base64: parameters.base64 != null,
}
}
2 changes: 1 addition & 1 deletion src/utils/parseUrlQueryString.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('parseUrlQueryString', () => {
).toEqual({
x: '1',
y: 'ooo',
base64: true,
base64: '',
msg: '',
})
})
Expand Down
6 changes: 4 additions & 2 deletions src/utils/parseUrlQueryString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ import { CreateUrlQueryStringOptions } from './createUrlQueryString'
* ```
*/
export function parseUrlQueryString<
T extends Record<string, any> = Record<string, any>
T extends Record<string, string> = Record<string, string>
>(query: string, options?: CreateUrlQueryStringOptions): T {
if (!query) return {} as any
const pairSeparator = options?.pairSeparator ?? '='
const partSeparator = options?.partSeparator ?? '&'
const parameters: T = {} as any
query = query.charAt(0) === '?' ? query.substring(1) : query
for (const pair of query.split(partSeparator)) {
const [key, value] = pair.split(pairSeparator)
if (!key) continue
const decodedKey = decodeURIComponent(key)
const decodedValue = value != null ? decodeURIComponent(value) : true
const decodedValue = value ? decodeURIComponent(value) : ''
;(parameters as any)[decodedKey] = decodedValue
}
return parameters
Expand Down

0 comments on commit b67d872

Please sign in to comment.