gaoluyang
9 天以前 86966ec9a83b93decbd93fc8ce2be1882d4c9775
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import type { Ref } from 'vue';
 
import type { AxiosProgressEvent, InfraFileApi } from '#/api/infra/file';
 
import { computed, unref } from 'vue';
 
import { useAppConfig } from '@vben/hooks';
import { $t } from '@vben/locales';
 
import { createFile, getFilePresignedUrl, uploadFile } from '#/api/infra/file';
import { baseRequestClient } from '#/api/request';
 
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
/**
 * 上传类型
 */
enum UPLOAD_TYPE {
  // 客户端直接上传(只支持S3服务)
  CLIENT = 'client',
  // 客户端发送到后端上传
  SERVER = 'server',
}
 
/**
 * 上传类型钩子函数
 * @param acceptRef 接受的文件类型
 * @param helpTextRef 帮助文本
 * @param maxNumberRef 最大文件数量
 * @param maxSizeRef 最大文件大小
 * @returns 文件类型限制和帮助文本的计算属性
 */
export function useUploadType({
  acceptRef,
  helpTextRef,
  maxNumberRef,
  maxSizeRef,
}: {
  acceptRef: Ref<string[]>;
  helpTextRef: Ref<string>;
  maxNumberRef: Ref<number>;
  maxSizeRef: Ref<number>;
}) {
  // 文件类型限制
  const getAccept = computed(() => {
    const accept = unref(acceptRef);
    if (accept && accept.length > 0) {
      return accept;
    }
    return [];
  });
  const getStringAccept = computed(() => {
    return unref(getAccept)
      .map((item) => {
        return item.indexOf('/') > 0 || item.startsWith('.')
          ? item
          : `.${item}`;
      })
      .join(',');
  });
 
  // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。
  const getHelpText = computed(() => {
    const helpText = unref(helpTextRef);
    if (helpText) {
      return helpText;
    }
    const helpTexts: string[] = [];
 
    const accept = unref(acceptRef);
    if (accept.length > 0) {
      helpTexts.push($t('ui.upload.accept', [accept.join(',')]));
    }
 
    const maxSize = unref(maxSizeRef);
    if (maxSize) {
      helpTexts.push($t('ui.upload.maxSize', [maxSize]));
    }
 
    const maxNumber = unref(maxNumberRef);
    if (maxNumber && maxNumber !== Infinity) {
      helpTexts.push($t('ui.upload.maxNumber', [maxNumber]));
    }
    return helpTexts.join(',');
  });
  return { getAccept, getStringAccept, getHelpText };
}
 
/**
 * 上传钩子函数
 * @param directory 上传目录
 * @returns 上传 URL 和自定义上传方法
 */
export function useUpload(directory?: string) {
  // 后端上传地址
  const uploadUrl = getUploadUrl();
  // 是否使用前端直连上传
  const isClientUpload =
    UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE;
  // 重写ElUpload上传方法
  async function httpRequest(
    file: File,
    onUploadProgress?: AxiosProgressEvent,
  ) {
    // 模式一:前端上传
    if (isClientUpload) {
      // 1.1 生成文件名称
      const fileName = await generateFileName(file);
      // 1.2 获取文件预签名地址
      const presignedInfo = await getFilePresignedUrl(fileName, directory);
      // 1.3 上传文件
      return baseRequestClient
        .put(presignedInfo.uploadUrl, file, {
          headers: {
            'Content-Type': file.type,
          },
        })
        .then(() => {
          // 1.4. 记录文件信息到后端(异步)
          createFile0(presignedInfo, file);
          // 通知成功,数据格式保持与后端上传的返回结果一致
          return { url: presignedInfo.url };
        });
    } else {
      // 模式二:后端上传
      return uploadFile({ file, directory }, onUploadProgress);
    }
  }
 
  return {
    uploadUrl,
    httpRequest,
  };
}
 
/**
 * 获得上传 URL
 */
export function getUploadUrl(): string {
  return `${apiURL}/infra/file/upload`;
}
 
/**
 * 创建文件信息
 *
 * @param vo 文件预签名信息
 * @param file 文件
 */
function createFile0(
  vo: InfraFileApi.FilePresignedUrlRespVO,
  file: File,
): InfraFileApi.File {
  const fileVO = {
    configId: vo.configId,
    url: vo.url,
    path: vo.path,
    name: file.name,
    type: file.type,
    size: file.size,
  };
  createFile(fileVO);
  return fileVO;
}
 
/**
 * 生成文件名称(使用算法SHA256)
 *
 * @param file 要上传的文件
 */
async function generateFileName(file: File) {
  // // 读取文件内容
  // const data = await file.arrayBuffer();
  // const wordArray = CryptoJS.lib.WordArray.create(data);
  // // 计算SHA256
  // const sha256 = CryptoJS.SHA256(wordArray).toString();
  // // 拼接后缀
  // const ext = file.name.slice(Math.max(0, file.name.lastIndexOf('.')));
  // return `${sha256}${ext}`;
  return file.name;
}