feat: add file upload in chat usage

pull/284/head
moonrailgun 2 years ago
parent e92c28bcd4
commit 52f70c89d5

@ -272,6 +272,6 @@ export {
applyDefaultFallbackGroupPermission, applyDefaultFallbackGroupPermission,
} from './utils/role-helper'; } from './utils/role-helper';
export { uploadFile } from './utils/upload-helper'; export { uploadFile } from './utils/upload-helper';
export type { UploadFileResult } from './utils/upload-helper'; export type { UploadFileResult, UploadFileUsage } from './utils/upload-helper';
export { parseUrlStr } from './utils/url-helper'; export { parseUrlStr } from './utils/url-helper';
export { sleep } from './utils/utils'; export { sleep } from './utils/utils';

@ -5,7 +5,10 @@ import { getGlobalConfig } from '../model/config';
import { showErrorToasts } from '../manager/ui'; import { showErrorToasts } from '../manager/ui';
import filesize from 'filesize'; import filesize from 'filesize';
export type UploadFileUsage = 'chat' | 'group' | 'user' | 'unknown';
interface UploadFileOptions { interface UploadFileOptions {
usage?: UploadFileUsage;
onProgress?: (percent: number, progressEvent: unknown) => void; onProgress?: (percent: number, progressEvent: unknown) => void;
} }
export interface UploadFileResult { export interface UploadFileResult {
@ -23,6 +26,7 @@ export async function uploadFile(
): Promise<UploadFileResult> { ): Promise<UploadFileResult> {
const form = new FormData(); const form = new FormData();
form.append('file', file); form.append('file', file);
form.append('usage', options.usage ?? 'unknown');
const uploadFileLimit = getGlobalConfig().uploadFileLimit; const uploadFileLimit = getGlobalConfig().uploadFileLimit;
if (file.size > uploadFileLimit) { if (file.size > uploadFileLimit) {

@ -39,7 +39,9 @@ export function uploadMessageImage(image: File): Promise<{
}%(${originImageSize} -> ${compressedImageSize})` }%(${originImageSize} -> ${compressedImageSize})`
); );
} }
const fileInfo = await uploadFile(uploadImage); const fileInfo = await uploadFile(uploadImage, {
usage: 'chat',
});
const imageRemoteUrl = fileInfo.url; const imageRemoteUrl = fileInfo.url;
resolve({ resolve({
@ -62,7 +64,9 @@ export async function uploadMessageFile(file: File): Promise<{
name: string; name: string;
url: string; url: string;
}> { }> {
const fileInfo = await uploadFile(file); const fileInfo = await uploadFile(file, {
usage: 'chat',
});
return { return {
name: file.name || fileInfo.etag, name: file.name || fileInfo.etag,

@ -1,13 +1,19 @@
import { blobUrlToFile } from '@/utils/file-helper'; import { blobUrlToFile } from '@/utils/file-helper';
import clsx from 'clsx'; import clsx from 'clsx';
import React, { PropsWithChildren, useState } from 'react'; import React, { PropsWithChildren, useState } from 'react';
import { uploadFile, UploadFileResult, useAsyncRequest } from 'tailchat-shared'; import {
uploadFile,
UploadFileResult,
UploadFileUsage,
useAsyncRequest,
} from 'tailchat-shared';
import { ImagePicker } from './ImagePicker'; import { ImagePicker } from './ImagePicker';
interface ImageUploaderProps extends PropsWithChildren { interface ImageUploaderProps extends PropsWithChildren {
circle?: boolean; circle?: boolean;
aspect?: number; aspect?: number;
className?: string; className?: string;
usage?: UploadFileUsage;
onUploadSuccess: (fileInfo: UploadFileResult) => void; onUploadSuccess: (fileInfo: UploadFileResult) => void;
} }
@ -20,6 +26,7 @@ export const ImageUploader: React.FC<ImageUploaderProps> = React.memo(
const file = await blobUrlToFile(blobUrl); const file = await blobUrlToFile(blobUrl);
const fileInfo = await uploadFile(file, { const fileInfo = await uploadFile(file, {
usage: props.usage,
onProgress(percent) { onProgress(percent) {
const uploadProgress = Number((percent * 100).toFixed()); const uploadProgress = Number((percent * 100).toFixed());
console.log(`进度:${uploadProgress}`); console.log(`进度:${uploadProgress}`);

@ -92,6 +92,7 @@ export const GroupConfig: React.FC<{
<> <>
<ImageUploader <ImageUploader
aspect={16 / 9} aspect={16 / 9}
usage="group"
onUploadSuccess={(fileInfo: UploadFileResult) => { onUploadSuccess={(fileInfo: UploadFileResult) => {
handleModifyConfig('groupBackgroundImage', fileInfo.url); handleModifyConfig('groupBackgroundImage', fileInfo.url);
}} }}

@ -65,6 +65,7 @@ export const GroupSummary: React.FC<{
<div className="w-1/3 mobile:w-full mobile:text-center"> <div className="w-1/3 mobile:w-full mobile:text-center">
<AvatarUploader <AvatarUploader
circle={true} circle={true}
usage="group"
onUploadSuccess={handleGroupAvatarChange} onUploadSuccess={handleGroupAvatarChange}
> >
<Avatar size={128} name={groupInfo.name} src={groupInfo.avatar} /> <Avatar size={128} name={groupInfo.name} src={groupInfo.avatar} />

@ -95,6 +95,7 @@ export const SettingsAccount: React.FC = React.memo(() => {
<div className="w-1/3 mobile:w-full"> <div className="w-1/3 mobile:w-full">
<AvatarUploader <AvatarUploader
circle={true} circle={true}
usage="user"
onUploadSuccess={handleUserAvatarChange} onUploadSuccess={handleUserAvatarChange}
> >
<Avatar size={128} src={userInfo.avatar} name={userInfo.nickname} /> <Avatar size={128} src={userInfo.avatar} name={userInfo.nickname} />

@ -163,6 +163,11 @@ export const fileFields = [
width: 140, width: 140,
}, },
}), }),
createTextField('usage', {
list: {
width: 100,
},
}),
createFileSizeField('size', { createFileSizeField('size', {
list: { list: {
width: 120, width: 120,
@ -170,12 +175,17 @@ export const fileFields = [
}, },
}), }),
createTextField('metaData.content-type'), createTextField('metaData.content-type'),
createTextField('etag'), createTextField('etag', {
list: {
width: 300,
},
}),
createUserField('userId', { createUserField('userId', {
reference: 'users', reference: 'users',
displayField: 'nickname', displayField: 'nickname',
list: { list: {
width: 80, width: 200,
ellipsis: true,
}, },
}), }),
createDateTimeField('createdAt', { createDateTimeField('createdAt', {

@ -58,6 +58,7 @@ export const zhTranslation = {
objectName: '对象存储名', objectName: '对象存储名',
url: '文件路径', url: '文件路径',
size: '文件大小', size: '文件大小',
usage: '使用场景',
'metaData.content-type': '文件类型', 'metaData.content-type': '文件类型',
userId: '存储用户', userId: '存储用户',
createdAt: '创建时间', createdAt: '创建时间',

@ -51,7 +51,7 @@ export const FileList: React.FC = React.memo(() => {
]} ]}
tableProps={{ tableProps={{
scroll: { scroll: {
x: 1200, x: 1600,
}, },
}} }}
fields={fileFields} fields={fileFields}

@ -61,6 +61,7 @@ export const SystemConfig: React.FC = React.memo(() => {
if (file) { if (file) {
const formdata = new FormData(); const formdata = new FormData();
formdata.append('file', file); formdata.append('file', file);
formdata.append('usage', 'server');
const { data } = await request.put('/file/upload', formdata, { const { data } = await request.put('/file/upload', formdata, {
headers: { headers: {

@ -56,6 +56,15 @@ export class File extends TimeStamps implements Base {
@prop() @prop()
metaData: object; metaData: object;
/**
*
* for example: chat, group, user
*/
@prop({
default: 'unknown',
})
usage: string;
} }
export type FileDocument = DocumentType<File>; export type FileDocument = DocumentType<File>;

@ -112,6 +112,7 @@ class FileService extends TcService {
ctx: TcContext< ctx: TcContext<
{}, {},
{ {
$multipart: any;
$params: any; $params: any;
filename: any; filename: any;
} }
@ -129,6 +130,7 @@ class FileService extends TcService {
} }
const originFilename = String(ctx.meta.filename); const originFilename = String(ctx.meta.filename);
const usage = _.get(ctx, 'meta.$multipart.usage', 'unknown');
const stream = ctx.params as NodeJS.ReadableStream; const stream = ctx.params as NodeJS.ReadableStream;
(stream as any).on('error', (err) => { (stream as any).on('error', (err) => {
@ -142,7 +144,8 @@ class FileService extends TcService {
const { etag, objectName, url } = await this.saveFileStream( const { etag, objectName, url } = await this.saveFileStream(
ctx, ctx,
originFilename, originFilename,
stream stream,
usage
); );
resolve({ resolve({
@ -211,7 +214,8 @@ class FileService extends TcService {
async saveFileStream( async saveFileStream(
ctx: TcContext, ctx: TcContext,
filename: string, filename: string,
fileStream: NodeJS.ReadableStream fileStream: NodeJS.ReadableStream,
usage = 'unknown'
): Promise<{ etag: string; url: string; objectName: string }> { ): Promise<{ etag: string; url: string; objectName: string }> {
const span = ctx.startSpan('file.saveFileStream'); const span = ctx.startSpan('file.saveFileStream');
const ext = path.extname(filename); const ext = path.extname(filename);
@ -234,7 +238,8 @@ class FileService extends TcService {
ctx, ctx,
tmpObjectName, tmpObjectName,
etag, etag,
ext ext,
usage
); );
span.finish(); span.finish();
@ -253,7 +258,8 @@ class FileService extends TcService {
ctx: TcContext, ctx: TcContext,
tmpObjectName: string, tmpObjectName: string,
etag: string, etag: string,
ext: string ext: string,
usage = 'unknown'
): Promise<{ ): Promise<{
url: string; url: string;
objectName: string; objectName: string;
@ -299,6 +305,7 @@ class FileService extends TcService {
url, url,
size: stat.size, size: stat.size,
metaData: stat.metaData, metaData: stat.metaData,
usage,
}, },
{ {
upsert: true, upsert: true,

Loading…
Cancel
Save