diff --git "a/source/_posts/\345\246\202\344\275\225\344\275\277\347\224\250nextjs\345\256\236\347\216\260\344\270\212\344\274\240pdf.md" "b/source/_posts/\345\246\202\344\275\225\344\275\277\347\224\250nextjs\345\256\236\347\216\260\344\270\212\344\274\240pdf.md"
new file mode 100644
index 0000000..708e632
--- /dev/null
+++ "b/source/_posts/\345\246\202\344\275\225\344\275\277\347\224\250nextjs\345\256\236\347\216\260\344\270\212\344\274\240pdf.md"
@@ -0,0 +1,107 @@
+---
+title: 如何使用nextjs实现上传pdf
+date: 2023-12-09 14:21:53
+tags:
+---
+之前研究gpt4-pdf-chatbot-langchain这个项目,觉得挺有意思,竟然可以让chatgpt去读一本书,还是js写的。不过这项目有一点不方便,他需要用nodejs解析pdf,然后用openai提供的api把api向量化,接着在浏览器里才能基于这本书的内容向chatgpt提问。
+
+然后我就想能不能把所有的操作入口都放在浏览器上,在浏览器上上传一个pdf,然后后端拿到上传的pdf,解析pdf,向量化,后面的操作和原项目一样。基于这样的想法,我写了以下的代码。
+
+```javascript
+//pages/index.tsx
+function App(){
+ const onFileUplload = e=>{
+ const file = event.target.files?.[0]
+ const formData = new FormData()
+ formData.append('file', file)
+ return fetch('/api/upload',{
+ method:'post',
+ body: formData,
+ }).then((resp)=> {
+ if(resp.status !== 200) return Promise.reject(resp.statusText)
+ return resp.json()
+ })
+ }
+ return (
+
+
+
+ )
+}
+```
+```javascript
+//pegae/api/upload.ts
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse,
+
+){
+ const buffer = Buffer.from(req.body)
+ console.log(buffer);
+ // 处理buffer,解析pdf
+}
+
+export const config = {
+ api:{
+ bodyParser: {
+ sizeLimit: '10mb'
+ }
+ }
+}
+```
+以上代码后续在解析pdf的过程中报错了,说我的pdf是加密的,很明显不是这个原因。经过一番查找发现,发现用以上代码拿到的buffer与直接用nodejs读pdf拿到的buffer长度不一样。
+
+buffer from browser upload
+![839ccc20-58c2-4830-94fb-f1ed9af5d960-buffer_from_upload.png](https://likaiqiang-blog.oss-cn-beijing.aliyuncs.com/images/839ccc20-58c2-4830-94fb-f1ed9af5d960-buffer_from_upload.png)
+
+buffer from nodejs
+![5a5738e6-2085-4381-953b-173466716bd9-image.png](https://likaiqiang-blog.oss-cn-beijing.aliyuncs.com/images/5a5738e6-2085-4381-953b-173466716bd9-image.png)
+
+显然,[buffer from browser upload]有问题,那这一步的buffer是从哪里来的,const buffer = Buffer.from(req.body),把这里的req.body打印出来看看,发现是个乱码的字符串,很明显不对,后续再把这个错误的字符串转换成buffer,后面的一系列步骤都错了。
+
+这个错误到底是怎么来的,怎么会拿到一个乱码的字符串呢?那就从源头查起, 我们知道浏览器上的http请求,不管请求体中是什么数据,最终都会转化为二进制,就是所谓的数字信号,然后数电转模电,单纯的数字信号是不能发射的,只能转换成模拟信号,就是我们说的波,发射出去。目标服务器在接收到这个波以后,需要把模拟信号转换成数字信号,就是二进制,然后才能处理这条http请求。数电模电之间的转换是不可能出错的,按照以上理论,我们在服务端拿到的这条请求应该是个二进制流才对,为什么会是乱码的字符串呢,所以,最大的可能就是nextjs处理了这个二进制流,但是没有处理好。所以我们需要拿到原始的二进制流。
+```javascript
+import { getBoundary, parse } from 'parse-multipart-data'
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse,
+
+){
+ const chunks: Uint8Array[] = []
+ let size = 0
+ const sizeLimit = 10 * 1024 * 1024
+
+ req.on('data',(chunk)=>{
+ size += chunk.length
+ if(size > sizeLimit){
+ res.status(413).send('payload too large')
+ req.connection.destroy()
+ return
+ }
+ chunks.push(chunk)
+ })
+ req.on('end',async ()=>{
+ const boundary = getBoundary(req.rawHeaders.join(';'))
+ const completeBuffer = Buffer.concat(chunks)
+ const parts = parse(completeBuffer, boundary)
+ const [file] = parts
+ console.log('file.data',file.data);
+ })
+ req.on('error',e=>{
+ console.log(e)
+ res.status(500).send('an error occurred')
+ })
+}
+
+export const config = {
+ api:{
+ bodyParser: false
+ }
+}
+```
+代码一目了然,先禁掉nextjs内置的bodyParser,直接监听req的data与end事件,收集chunk,然后通过getBoundary与parse从formdata中解析出file,这里log出的file.data就是正确的数据,长度是1086938。
+
+拿到正确的buffer以后就可以调用pdf-parser解析了。
+
+
+
diff --git "a/source/_posts/\346\225\264\347\220\206react\351\207\214\351\235\242\351\202\243\344\272\233\345\256\271\346\230\223\350\256\251\344\272\272\346\267\267\346\267\206\347\232\204\346\246\202\345\277\265-u-1F602.md" "b/source/_posts/\346\225\264\347\220\206react\351\207\214\351\235\242\351\202\243\344\272\233\345\256\271\346\230\223\350\256\251\344\272\272\346\267\267\346\267\206\347\232\204\346\246\202\345\277\265-u-1F602.md"
index 9bcedc3..a8df60c 100644
--- "a/source/_posts/\346\225\264\347\220\206react\351\207\214\351\235\242\351\202\243\344\272\233\345\256\271\346\230\223\350\256\251\344\272\272\346\267\267\346\267\206\347\232\204\346\246\202\345\277\265-u-1F602.md"
+++ "b/source/_posts/\346\225\264\347\220\206react\351\207\214\351\235\242\351\202\243\344\272\233\345\256\271\346\230\223\350\256\251\344\272\272\346\267\267\346\267\206\347\232\204\346\246\202\345\277\265-u-1F602.md"
@@ -263,9 +263,9 @@ useState、useMemo、useCallback、useRef、useContext、useReducer、useEffect
### commit阶段的hooks
useEffect与useLayoutEffect的回调函数。
-先说useLayoutEffect。这个hook会在dom更新完毕,但是浏览器还没有来得及绘制之前同步执行。这句话是什么意思呢,我们知道javascript只是用来操作dom,像setState之类操作或者我们手动操作dom只是改变了内存里一颗dom树上的某些节点,真正把dom树绘制成屏幕上形形色色的画面是浏览器完成的,假设我们浏览器一秒绘制60帧,那绘制一帧的时间就是16ms,而javascript是单线程语言,执行js代码与ui绘制是互斥的,所以如果我们在16ms内用10ms的时间来更新dom树,那么还剩最多6ms用来执行useLayoutEffect。
+先说useLayoutEffect。这个hook会在dom更新完毕,但是浏览器还没有来得及绘制之前同步执行。这句话是什么意思呢,我们知道javascript只是用来操作dom,像setState之类操作或者我们手动操作dom只是改变了内存里一颗dom树上的某些节点,真正把dom树绘制成屏幕上形形色色的画面是浏览器完成的,假设我们浏览器一秒绘制60帧,那绘制一帧的时间就是16ms,而javascript是单线程语言,执行js代码与ui绘制是互斥的,所以如果我们在16ms内用10ms的时间来更新dom树,那么还剩6ms用来执行useLayoutEffect与浏览器绘制界面。
-所以这个hook内部适合执行一些短小精悍的代码,假如执行时间过长,那这一次更新就没有被绘制,人就会感觉到卡。
+所以这个hook内部适合执行一些短小精悍的代码,假如执行时间过长,浏览器没有足够的时间在一帧内完成界面绘制,就会造成卡顿。
对比介绍类组件时也有个在commit阶段执行的函数,getSnapshotBeforeUpdate,useLayoutEffect与getSnapshotBeforeUpdate的执行时机却有细微的差别,前者是在真实dom更新完毕浏览器还未绘制之前触发,这时拿到的是更新后的dom,后者是在真实dom还未更新即将更新时触发,这时拿到的是更新前的dom。