Skip to content

Commit 5ccf5eb

Browse files
committed
feat: 画像処理ライブラリsharpを追加し、Blobからの画像取得をURL形式に変更
1 parent b60a0af commit 5ccf5eb

4 files changed

Lines changed: 103 additions & 46 deletions

File tree

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"react-icons": "^5.2.1",
3030
"react-markdown": "^9.0.1",
3131
"react-redux": "^9.1.2",
32+
"sharp": "^0.34.1",
3233
"tailwindcss": "3.4.3",
3334
"typescript": "5.4.5",
3435
"uuid": "^11.1.0"

frontend/src/util/blob.ts

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,102 @@
11
// BlobStorage
22
import {
3-
BlobServiceClient,
4-
StorageSharedKeyCredential
3+
BlobServiceClient,
4+
StorageSharedKeyCredential
55
} from '@azure/storage-blob';
6+
import sharp from 'sharp'; // 画像処理ライブラリを追加
67

78
// ファイルを取得する
89
// output: base64
910
export const getBase64File = async (file_path: string): Promise<string> => {
10-
return new Promise(async (resolve, reject) => {
11-
const sharedKeyCredential = new StorageSharedKeyCredential(
12-
process.env.AZURE_STORAGE_ACCOUNT_NAME!,
13-
process.env.AZURE_STORAGE_ACCOUNT_ACCESS_KEY!
14-
);
15-
const blobServiceClient = new BlobServiceClient(
16-
`https://${process.env.AZURE_STORAGE_ACCOUNT_NAME}.blob.core.windows.net`,
17-
sharedKeyCredential
18-
);
19-
const containerClient = blobServiceClient.getContainerClient(process.env.AZURE_STORAGE_CONTAINER_NAME!);
20-
const blobClient = containerClient.getBlobClient(file_path);
21-
22-
//blobからデータをダウンロード
23-
const downloadResponse = await blobClient.download(0);
24-
25-
//データを文字列に
26-
let encodedData = '';
27-
if (downloadResponse.readableStreamBody) {
28-
const downloaded = await streamToBuffer(downloadResponse.readableStreamBody);
29-
const encodedData = downloaded.toString('base64');
30-
resolve(encodedData);
31-
} else {
32-
reject('readableStreamBody is undefined');
33-
}
34-
35-
resolve(encodedData);
36-
37-
});
11+
return new Promise(async (resolve, reject) => {
12+
const sharedKeyCredential = new StorageSharedKeyCredential(
13+
process.env.AZURE_STORAGE_ACCOUNT_NAME!,
14+
process.env.AZURE_STORAGE_ACCOUNT_ACCESS_KEY!
15+
);
16+
const blobServiceClient = new BlobServiceClient(
17+
`https://${process.env.AZURE_STORAGE_ACCOUNT_NAME}.blob.core.windows.net`,
18+
sharedKeyCredential
19+
);
20+
const containerClient = blobServiceClient.getContainerClient(process.env.AZURE_STORAGE_CONTAINER_NAME!);
21+
const blobClient = containerClient.getBlobClient(file_path);
22+
23+
// blobからデータをダウンロード
24+
const downloadResponse = await blobClient.download(0);
25+
26+
// データを文字列に
27+
if (downloadResponse.readableStreamBody) {
28+
const downloaded = await streamToBuffer(downloadResponse.readableStreamBody);
29+
30+
// 画像を縮小
31+
const resizedBuffer = await resizeImage(downloaded);
32+
33+
const mimeType = getMimeType(file_path); // MIMEタイプを取得
34+
const encodedData = `data:${mimeType};base64,${resizedBuffer.toString('base64')}`;
35+
resolve(encodedData);
36+
} else {
37+
reject('readableStreamBody is undefined');
38+
}
39+
});
3840
};
3941

42+
// BlobのURLを取得する
43+
export const getBlobUrl = async (file_path: string): Promise<string> => {
44+
const sharedKeyCredential = new StorageSharedKeyCredential(
45+
process.env.AZURE_STORAGE_ACCOUNT_NAME!,
46+
process.env.AZURE_STORAGE_ACCOUNT_ACCESS_KEY!
47+
);
48+
const blobServiceClient = new BlobServiceClient(
49+
`https://${process.env.AZURE_STORAGE_ACCOUNT_NAME}.blob.core.windows.net`,
50+
sharedKeyCredential
51+
);
52+
const containerClient = blobServiceClient.getContainerClient(process.env.AZURE_STORAGE_CONTAINER_NAME!);
53+
const blobClient = containerClient.getBlobClient(file_path);
54+
55+
// BlobのURLを生成
56+
return blobClient.url;
57+
};
58+
59+
// 画像を縮小する関数
60+
async function resizeImage(buffer: Buffer): Promise<Buffer> {
61+
try {
62+
return await sharp(buffer)
63+
.resize({ width: 800 }) // 幅を800pxに縮小(必要に応じて調整)
64+
.toBuffer();
65+
} catch (error) {
66+
console.error('画像の縮小に失敗しました:', error);
67+
throw error;
68+
}
69+
}
70+
71+
// ファイルパスからMIMEタイプを推測
72+
function getMimeType(filePath: string): string {
73+
const extension = filePath.split('.').pop()?.toLowerCase();
74+
switch (extension) {
75+
case 'jpg':
76+
case 'jpeg':
77+
return 'image/jpeg';
78+
case 'png':
79+
return 'image/png';
80+
case 'gif':
81+
return 'image/gif';
82+
case 'txt':
83+
return 'text/plain';
84+
case 'pdf':
85+
return 'application/pdf';
86+
default:
87+
return 'application/octet-stream';
88+
}
89+
}
90+
4091
async function streamToBuffer(readableStream: NodeJS.ReadableStream): Promise<Buffer> {
41-
return new Promise((resolve, reject) => {
42-
const chunks: Buffer[] = [];
43-
readableStream.on('data', (data) => {
44-
chunks.push(data instanceof Buffer ? data : Buffer.from(data));
45-
});
46-
readableStream.on('end', () => {
47-
resolve(Buffer.concat(chunks));
48-
});
49-
readableStream.on('error', reject);
50-
});
92+
return new Promise((resolve, reject) => {
93+
const chunks: Buffer[] = [];
94+
readableStream.on('data', (data) => {
95+
chunks.push(data instanceof Buffer ? data : Buffer.from(data));
96+
});
97+
readableStream.on('end', () => {
98+
resolve(Buffer.concat(chunks.map(chunk => Uint8Array.from(chunk))));
99+
});
100+
readableStream.on('error', reject);
101+
});
51102
}

frontend/src/util/generate.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getEmbedding, getChatCompletions, getQueryJson } from './openai';
22
import { getItemsByVector } from './cosmos/document';
33
import { getCategoryById } from './cosmos/category';
4-
import { getBase64File } from './blob';
4+
import { getBlobUrl } from './blob';
55
import { Query } from '../models/models';
66
import { CosmosInferenceItem } from "../models/models";
77
import { APIMessagesType } from "@/types/types";
@@ -56,8 +56,8 @@ export const getInferenceRAG = async (messages: APIMessagesType[], message: stri
5656
systemMessage += '# ' + (CosmosInferenceItems.indexOf(result) + 1) + " " + categoryName + " " + result.file_name + '\n' + result.content + '\n\n';;
5757
// 画像の取得
5858
if (result.is_contain_image === true) {
59-
const image = await getBase64File(result.image_blob_path);
60-
images.push(image);
59+
const imageUrl = await getBlobUrl(result.image_blob_path);
60+
images.push(imageUrl);
6161

6262
responseImageUrl += result.image_blob_path + ': ' + result.SimilarityScore + ' \n';
6363
}

frontend/src/util/openai.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const vectorDeployment = process.env.AZURE_OPENAI_VEC_DEPLOYMENT_ID!;
1010
const apiVersion = "2024-10-21";
1111

1212
export const getChatCompletions = async (system_message: string, message: string, messages?: APIMessagesType[], images?: string[]): Promise<any[]> => {
13-
console.log('start', process.env.AZURE_OPENAI_ENDPOINT!);
1413
return new Promise(async (resolve, reject) => {
1514
const client = new AzureOpenAI({
1615
endpoint,
@@ -33,10 +32,10 @@ export const getChatCompletions = async (system_message: string, message: string
3332
type: "text",
3433
text: message
3534
},
36-
...(images ?? []).map(image => ({
35+
...(images ?? []).map(imageUrl => ({
3736
type: "image_url",
3837
image_url: {
39-
url: `data:image/jpeg;base64,${image}`
38+
url: imageUrl
4039
}
4140
}))
4241
])
@@ -66,6 +65,12 @@ export const getChatCompletions = async (system_message: string, message: string
6665
};
6766

6867
try {
68+
console.log(" 🚀Azure OpenAIへのリクエスト開始:", deployment_2);
69+
console.log(" system_message:", system_message);
70+
console.log(" message:", message);
71+
console.log(" messages:", messages);
72+
// 画像の数
73+
console.log(" images:", images?.length ?? 0);
6974
const response = await createCompletion(deployment_2);
7075
resolve(response.choices);
7176
} catch (error: any) {

0 commit comments

Comments
 (0)