1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        同步用戶微信頭像的 NodeJs 實(shí)現(xiàn)

        共 6909字,需瀏覽 14分鐘

         ·

        2021-07-03 22:54

        對(duì)于使用微信登錄的系統(tǒng),在用戶授權(quán)后,將其微信頭像直接同步到服務(wù)器,可以省去用戶上傳的操作。本文最終給出一個(gè) NodeJs 中間層的實(shí)現(xiàn),并展示實(shí)現(xiàn)的過程和在實(shí)施過程中幾個(gè)需要注意的地方。


        BFF 架構(gòu)


        微服務(wù)架構(gòu)已然成為了企業(yè)信息化架構(gòu)中的主流,這種架構(gòu)風(fēng)格給前端帶來了挑戰(zhàn)。為了靈活應(yīng)對(duì)業(yè)務(wù)需求的變化和適配不同的前端用戶體驗(yàn),BFF 層應(yīng)運(yùn)而生。

        由于天然的限制或者使用場(chǎng)景的區(qū)別,不同的前端用戶體驗(yàn)并不一致。拿阿迪達(dá)斯的微信小程序和其原生 APP 舉例,你會(huì)看到用戶體驗(yàn)完全不同,有些是因?yàn)槲⑿判〕绦虻南拗疲ū热绶窒眢w驗(yàn)),有些是不同的產(chǎn)品運(yùn)營需要。


        小程序

        APP


        BFF 是 Backend for Frontend 的簡稱,它用來對(duì)眾多后端微服務(wù)進(jìn)行聚合和裁剪,以適配前端。如今,端用戶體驗(yàn)層 -> 網(wǎng)關(guān)層 -> BFF 層 -> 微服務(wù)層這種分層模式已經(jīng)成為了典型的現(xiàn)代微服務(wù)架構(gòu)分層方式。


        NodeJs


        NodeJs 的出現(xiàn)使得 JavaScript 可以運(yùn)行在服務(wù)器上,并且天然適合網(wǎng)絡(luò) IO 密集型的場(chǎng)景,以及不適合計(jì)算密集型場(chǎng)景。這使得它作為 BFF 層非常適合,因?yàn)?BFF 層通常只是聯(lián)結(jié)前端與后端,做一些透?jìng)鳎瑳]有密集的計(jì)算,但是重網(wǎng)絡(luò)傳輸。


        分析微信頭像的存儲(chǔ)方案


        直接存儲(chǔ)微信頭像的 url


        比如,我目前的微信頭像 url 是 https://thirdwx.qlogo.cn/mmopen/vi_32/rgPgbf5XE2ancz9ibobSibZEMPOibp4LdsQEXiaQeRZ78WJgVe7xgMamYXd6eibo9rg0Wje1rnh9aLMc87DVS4vrItA/132。顯然后端可以很簡單的接收一個(gè)字符串,將其存儲(chǔ)起來,這樣前端下次拿到這個(gè) url,就可以展示出來。


        但是這樣做有個(gè)問題,以上鏈接是微信的 CDN 地址。一旦用戶在微信端更新了頭像,那么上面的地址將不再被使用。如果某一天它被清除了,那么系統(tǒng)的前端展示用戶頭像時(shí)將是一個(gè)死鏈接的圖片。所以方案得改成:


        將微信頭像 url 下載下來以圖片文件格式存儲(chǔ)


        這樣就需要后端實(shí)現(xiàn)一個(gè)文件上傳的接口,然后由 BFF 層把前端傳過來的 url 轉(zhuǎn)成表單數(shù)據(jù)傳輸給后端。所以最終后端不是存儲(chǔ)一個(gè)字符串,而是存儲(chǔ)圖片文件。

        這樣就沒有用戶更改微信頭像后,系統(tǒng)中的頭像失效的問題。至于微信頭像更新后,系統(tǒng)中還是老的圖片的不同步問題,第一種方案也不能解決。實(shí)際上這種情況下只需要再次同步即可,至于如何自動(dòng)同步,不在本文討論范圍內(nèi)。


        結(jié)論


        只需要在 BFF 層使用 NodeJs 將微信頭像的 url 下載下來,再調(diào)用后端的文件上傳接口即可。


        代碼實(shí)現(xiàn)


        需求分析明確后,只差寫代碼了。經(jīng)常有人問,高手寫代碼是不是不用百度,直接啪啪啪就能寫出來?實(shí)際上,不需要搜索就能寫代碼的,那說明是熟練工,同樣的事情干過很多回了。對(duì)于高手,也可能接到不熟悉的任務(wù),這時(shí)他可能不用百度,而是用 Google 和 StackOverflow。


        Axios


        既然要使用 NodeJs 上傳文件到后端,那么就需要給后端發(fā)起一個(gè) Http 請(qǐng)求。通過簡單搜索就能知道在 NodeJs 的世界里,Axios 是一個(gè)不錯(cuò)的 Http 客戶端,因此再進(jìn)一步搜索如何使用 Axios 發(fā)起一個(gè)文件上傳的 Http 請(qǐng)求。



        搜索工具是程序員經(jīng)常要使用的,雖然說如今搜索方便,但是要甄別結(jié)果的可靠性并沒那么容易。被一些答案帶到坑里是常有的事情。比如搜索使用 NodeJs 上傳文件,多數(shù)答案如下:



        var formData = new FormData();

        formData.append("image", yourFile);

        axios.post('upload_file', formData, {

            headers: {

              'Content-Type': 'multipart/form-data'

            }

        })


        注意上面的代碼顯式指定了 Content-Type 這個(gè)請(qǐng)求頭,然后實(shí)際試過后你就知道這并不工作!


        Postman


        Postman 是一個(gè)強(qiáng)大的 Http 請(qǐng)求監(jiān)控工具,可以按需定制請(qǐng)求體。BFF 層要同步微信頭像,無非就是要調(diào)用后端接口,發(fā)送一個(gè) Http 請(qǐng)求,將用戶頭像存儲(chǔ)起來。因此真正的高手對(duì)這個(gè)需求是真的不會(huì)去搜索的,而是直接使用 Postman 構(gòu)造一個(gè) Http 請(qǐng)求,手動(dòng)上傳文件,拿到后端的響應(yīng)結(jié)果。



        然后,點(diǎn)擊代碼,就能選擇將剛才手動(dòng)構(gòu)造的 Http 請(qǐng)求,轉(zhuǎn)換成可以構(gòu)造同樣請(qǐng)求的代碼。我們選擇 NodeJs Axios:



        抄作業(yè)


        從 Postman 生成的代碼可以看出,第一 NodeJs 的世界里,沒有原生的表單數(shù)據(jù)結(jié)構(gòu),需要引入 form-data 包;第二在請(qǐng)求頭里不能直接寫死 Content-Type = 'multipart/form-data',而是要用 form-data 生成的請(qǐng)求頭。


        題外話


        如果是前端直接文件上傳,那么在 Browser 的 JavaScript 世界里,是自帶 FormData 數(shù)據(jù)結(jié)構(gòu)的,這時(shí)候要顯式不指定 Content-Type,以實(shí)現(xiàn)自動(dòng)生成 Content-Type 請(qǐng)求頭。對(duì)于文件上傳不能顯示指定 Content-Type 的原因是,構(gòu)造 Http 請(qǐng)求時(shí),payload 中要使用 Content-Type 請(qǐng)求頭中的 boundary 來分割文件和其他非文件字段,而這個(gè) boundary 需要?jiǎng)討B(tài)生成。如果不顯示指定 Content-Type,就能享受瀏覽器端 FormData 或者 NodeJs 端的 form-data 自動(dòng)生成的 Content-Type 以及 boundary。


        TDD


        在寫實(shí)現(xiàn)代碼前,建議先將自動(dòng)化測(cè)試代碼寫上,以便構(gòu)建重構(gòu)屏障。詳細(xì)步驟參考 TDD 相關(guān)的文章。


        jest/nock/TypeScript


        在實(shí)際的 NodeJs 工程項(xiàng)目中,還是建議引入 TypeScript,以享受類型系統(tǒng)帶來的好處。這里使用 jest 測(cè)試框架。為了控制后端的 Http 響應(yīng),可以使用 nock 將之前的 Postman 抓到的后端服務(wù)器響應(yīng)作為 mock。


        后端服務(wù)器的 API 可能做了 token 驗(yàn)證,只信任指定的客戶端(BFF 層)發(fā)來的請(qǐng)求,因此還需要做好相關(guān) Token 端點(diǎn)的 nock,最終測(cè)試代碼如下(假定要將實(shí)現(xiàn)寫在一個(gè)叫 MemberService 的類中):


        import { MemberService } from './member.service'

        import * as nock from 'nock'


        describe('MemberService', () => {

          beforeEach(async () => {

            const mockConfig = {

              backend: {

                url: 'https://your.back.end',

                auth: {

                  url: 'https://your.back.end/auth/token',

                  clientId: 'fakeId',

                  clientSecret: 'fakeSecret',

                  clientKey: 'fakeKey'

                }

              }

            }

           

            describe('update user\'s head image', () => {

              it('pipe weixin head img to back end', async () => {

                const mockRes = {

                  code: 200,

                  message: '操作成功',

                  success: true,

                  data: 'https://upload.image.url',

                  time: '2021-06-29 11:20:30'

                }

               

                nock(mockConfig.backend.url).post('/auth/token').reply(200, {status: 'SUCCESS', data: {access_token: 'xxx', expires_in: 3600, refresh_token: 'yyy'}})

                nock(mockConfig.backend.url).put('/upload/image/head/abcdefg').reply(200, mockRes)

               

                const sut = new MemberService(nockConfig)

               

                const res = await sut.updateAvatar('abcdefg', 'https://thirdwx.qlogo.cn/mmopen/vi_32/rgPgbf5XE2ancz9ibobSibZEMPOibp4LdsQEXiaQeRZ78WJgVe7xgMamYXd6eibo9rg0Wje1rnh9aLMc87DVS4vrItA/132')

                expect(res).toStrictEqual(mockRes)

              })

            })

          })

        })


        流到流


        前面分析了,實(shí)現(xiàn)代碼只需要將微信的 url 對(duì)應(yīng)的圖片下載下來,再上傳到后端服務(wù)器即可,但是為了提高效率,可以不用等待先全部下載完畢再進(jìn)行上傳,而是將下載流直接對(duì)接到上傳流上。這只需要對(duì) Postman 生成的代碼稍加改造。仔細(xì)觀察 Postman 生成的代碼,由于我們是從本地文件系統(tǒng)選擇的文件構(gòu)造出的請(qǐng)求,因此生成的代碼創(chuàng)建了一個(gè)本地文件讀取流,我們需要把這個(gè)本地文件讀取流改造成遠(yuǎn)程文件下載流。


        下載文件其實(shí)也就是向微信服務(wù)器(CDN)端構(gòu)造一個(gè) Http GET 請(qǐng)求,仍然采用 Axios,那么只需要設(shè)置 responseType 為 stream,就能得到文件下載流:


        import axios from 'axios'

        import * as FormData from 'form-data'


        export class MemberService {

          constructor(private readonly config: Config) {}

         

          async updateAvatar(userId: string, avatar: string | undefined) {

            if (!avatar) {

              return undefined

            }

           

            // 大致邏輯,實(shí)際上從統(tǒng)一的令牌管理類中拿可用的 token

           

            const {data: {access_token}} = await axios.post(this.config.backend.auth.url, {clientId, clientSecret, ...})

           

            const formData = new FormData()

            formData.append('headImg', (await axios.get(avatar, { responseType: 'stream' })).data, 'headImage.jpg')

           

            return axios.put(`${this.config.backend.url}/upload/image/head/${userId}`, {

              data: formData,

              headers: {

                'Authorization': `Bearer ${access_token}`,

                ...formData.getHeaders(),

              }

            })

          }

        }


        總結(jié)


        在實(shí)際的 BFF 開發(fā)中,可以使用 Postman 手動(dòng)調(diào)用后端服務(wù),然后生成實(shí)際的代碼,這節(jié)省了搜索的工作,而且保證代碼可靠。


        對(duì)于微信頭像的同步,一定不能只保存微信的 CDN url,而要下載后保存圖片。通過使用 NodeJs Axios,下載到上傳是可以很方便地流到流接上的。


        瀏覽 85
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            俺也去俺来也www色官网 | 免费一级看片 | 国产激情久久久 | 国产天堂123在线观看 | 91蝌蚪网| 警花尤物在胯下疯狂呻吟娇喘 | 国产日韩一级片 | 嫩草香蕉在线91一二三区 | 亚洲免费人成在线观看网站 | www.欧美黄 |