cocos creator + nestjs 上傳下載文件功能

上傳文件

  1. 客戶端

    打開本地文件
    // ts 代碼
    // 選擇本地文件,以下callback爲函數回調參數
    public static openLocalFile(callback: (file: File) => void) {
        let inputEl: HTMLInputElement = <HTMLInputElement>document.getElementById('file_input');// 類型轉行 HTMLInputElement ,方便下面的 inputEl.files 調用
        if (!inputEl) {
            // 只創建一次
            console.log('xxxxxx createElement input');
            inputEl = document.createElement('input');
            inputEl.id = 'file_input';
            inputEl.setAttribute('id', 'file_input');
            inputEl.setAttribute('type', 'file');
            inputEl.setAttribute('class', 'fileToUpload');
            inputEl.style.opacity = '0';// 不可見
            inputEl.style.position = 'absolute';
            document.body.appendChild(inputEl);
        }
        // 這個和 inputEl.onchange 的效果是一樣的,2選1就可以了
        // inputEl.addEventListener('change', (event) => {
        //    console.log('xxx onchange1', event, inputEl.value);
        // });
        inputEl.onchange = (event) => {
            // console.log('xxx onchange2', event, inputEl.files);
            let files = inputEl.files;// 我在Mac上測試,每次只能選一個文件
            if (files && files.length > 0) {
                var file = files[0];
                if (callback) callback(file);
            }
        }
        inputEl.click();// 模擬點擊,觸發文件選擇彈出框,據說有的瀏覽器不支持,chrome是沒問題的
    }

    發送上傳文件消息

    // ts 代碼
    public upload(file: File) {
        // 使用表單設置文件,發送上傳消息到服務器
        let forms = new FormData();
        forms.append("file", file);// 必填,key不限制必須"file",根據nestjs服務器邏輯填寫
        forms.append('fileName', file.name);// 選填,根據nestjs服務器邏輯填寫
        forms.append('targetPath', 'test');// 選填,根據nestjs服務器邏輯填寫
        let xhr = cc.loader.getXMLHttpRequest();//new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status >= 200 && xhr.status <= 300 || xhr.status == 304) {
                    console.log(xhr.response)
                }
            } else {
                console.log(xhr.status)
            }
        }
        xhr.open('POST', 'http://localhost:443/api/v1/upload', true);
        // xhr.setRequestHeader 的問題卡了好久,
        // nestjs那邊一直報錯,nestjs的文檔要求是 multipart/form-data 格式,
        // 但是要是你自己設置的話會導致 Boundary 丟失,nestjs 的multer中間件解析錯誤,報錯"Multipart: Boundary not found",
        // 所以不設置"multipart/form-data",自動生成就好,可以在network裏查看發送的消息頭已經自動添加好了"Content-Type",
        // 網上各路大神各種花式解決方案,都未解決我的困惑,最後參考這個鏈接解決,跪謝https://blog.csdn.net/yun_hou/article/details/97004557
        // xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");// multer不會處理非"multipart/form-data"表單
        // xhr.setRequestHeader("Content-Type", "multipart/form-data");
        // console.log(forms);// 打印不出來什麼的,所以放棄吧
        xhr.addEventListener("progress", function (evt) {
            console.log('xxx progress', evt);// 上傳進度,我只看到打印一次
        }, false);
        xhr.send(forms);
    }

    本地圖片通過sprite展示

    // ts 代碼
    // 展示本地圖片到sprite
    public showImage(file: File, sprite: cc.Sprite) {
        if (!file) return;
        // 讀取文件爲base64數據流
        readFile(file, (base64: string) => {
            base64ToSpriteFrame(base64, (spriteFrame: cc.SpriteFrame) => {
                if (sprite) sprite.spriteFrame = spriteFrame;
            });
        });
    }
    // 讀取文件爲base64數據流
    public readFile(file: File, callback: (base64: string) => void) {
        var reader = new FileReader();
        reader.onload = function (event2) {
            if (callback) {
                if (reader.readyState == FileReader.DONE) {
                    // console.log('xxx FileReader', event2, reader.result);
                    callback(reader.result.toString());
                } else {
                    console.error('FileReader read error', event2, reader.result);
                    callback(null);
                }
            }
        };
        // reader.readAsText(file);//作爲字符串讀出
        reader.readAsDataURL(file);
        //reader.readAsText(file,'gb2312');//默認是用utf-8格式輸出的,想指定輸出格式就再添加一個參數,像txt的ANSI格式只能用國標才能顯示出來
    }
    // 
    public base64ToSpriteFrame(base64: string, callback: (this: void, spriteFrame: cc.SpriteFrame) => void) {
            var img = new Image();
            console.warn("------------準備合成-------------")
            img.onload = function () {
                console.warn("*********img.onload**********")
                var texture = new cc.Texture2D();
                texture.initWithElement(img);
                texture.handleLoadedTexture();
                var newframe = new cc.SpriteFrame(texture);
                if (callback) callback(newframe);
            }
            img.onerror = function (err) {
                console.warn("------合成報錯-----", err)
            }
            if ((<any>base64).startsWith !== undefined && (<any>base64).startsWith("data:image")) {
                img.src = base64;
            } else {
                img.src = "data:image/png;base64," + base64;
            }
        }

     

  2. 服務器

    // nestjs
    // main.ts
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import * as cors from 'cors';
    
    // import { NestExpressApplication } from '@nestjs/platform-express';
    
    
    
    async function bootstrap() {
        const app = await NestFactory.create(AppModule);
        // const app = await NestFactory.create< NestExpressApplication>(AppModule);// 有說新版本要這麼寫,我的nestjs版本是7.1.2,沒這麼寫也能用
        // 跨域
        app.use(cors({
            origin: '*',
            credentials: true
        }));
        await app.listen(3000);
    }
    bootstrap();
    // nestjs 單文件上傳處理事例,這個搞通了多文件參考網上其他教程就也差不多能搞出來了
    // app.controller.ts
    import { Controller, Get, Post, Body, UseInterceptors, UploadedFile } from '@nestjs/common';
    import { AppService } from './app.service';
    
    import { FileFieldsInterceptor, FileInterceptor } from '@nestjs/platform-express';
    import { createWriteStream } from 'fs';
    import { join } from 'path';
    // 網上好多教程都不給import部分,對於一個小白來說我知道 createWriteStream 和 join 是個鬼哦
    
    @Controller("api/v1")
    export class AppController {
        constructor(private readonly appService: AppService) {}
    
        @Post("upload")
        @UseInterceptors(FileInterceptor('file'))
        async uploadFile(@UploadedFile() file, @Body() body) {
            console.log('upload file', file);
            // console.log('upload body', body);
            // 這個文件路徑是參照本文件的,路徑不對的話多試幾次吧,或者自己加個路徑檢測的代碼,缺少對應路徑記得自己創建哈
            const writeImage = createWriteStream(join(__dirname, '../../../public/upload/', `${file.originalname}`));
            writeImage.write(file.buffer);
            return '上傳圖片成功';
        }
    }

     

下載文件

  1.  客戶端

    // ts 代碼
    // cocos creator 客戶端下載
    downloadImage(fileName: string, sprite: cc.Sprite) {
            let progressCallback = (completedCount: number, totalCount: number, item: any): void=> {
                console.log('download progressCallback', completedCount, totalCount, item);
            }
            let completeCallback = (error: Error, resource: any): void => {
                console.log('download completeCallback', error, resource);
                if (resource && sprite) sprite.spriteFrame  = new cc.SpriteFrame(resource);
            }
            cc.loader.load('http://localhost:443/api/v1/download/' + fileName, progressCallback, completeCallback);
            // cc.loader.loadRes只能加載resources路徑下的文件
            // cc.loader.getXMLHttpRequest() 需要自己做類型轉換,有需要再嘗試吧
    // let xhr = cc.loader.getXMLHttpRequest();//new XMLHttpRequest();
            // xhr.onreadystatechange = function () {
            //     if (xhr.readyState == 4) {
            //         // if (xhr.status >= 200 && xhr.status <= 300 || xhr.status == 304) {
            //         //     console.log(xhr.response);
            //         // }
            //         if (xhr.status == 200) {
            //              console.log(xhr.response);
            //         }
            //     } else {
            //         // console.log(xhr.status);
            //     }
            // }
            // xhr.responseType = 'arraybuffer';
            // xhr.open('GET', 'http://localhost:443/api/v1/download/' + fileName, true);
            // xhr.send();
    }

     

  2. 服務器

    // ts 代碼
    // nestjs 服務器下載靜態資源代碼,可以接着前面的上傳代碼寫
    import { Controller, Get, Param, Res } from '@nestjs/common';
    import { AppService } from './app.service';
    
    import { join } from 'path';
    import { Response } from 'express';// 一開始我一直以爲 Response 是 '@nestjs/common' 裏的,呵呵,sendFile一直報錯,後來.....參考https://stackoverflow.com/questions/55325062/how-to-serve-static-html-files-in-nest-js
    
    @Controller("api/v1")
    export class AppController {
        constructor(private readonly appService: AppService) {}
    
        @Get("download/:file")
        getFile(@Res() res: Response, @Param() params): string {
            console.log('xxx', params);
            res.sendFile(join(__dirname, '../../../public/upload', params.file));
            return "下載成功";
        }
    }

     

 

參考

  1. https://docs.nestjs.com/techniques/configuration#custom-env-file-path
  2. https://cloud.tencent.com/developer/section/1490184
  3. https://www.cnblogs.com/lengyue0030/p/6885107.html
  4. https://www.jianshu.com/p/eec0586409da
  5. https://www.cnblogs.com/ajanuw/p/9575278.html
  6. https://www.cnblogs.com/web-wjg/p/9244074.html
  7. https://blog.csdn.net/weixin_44273392/article/details/100153051
  8. https://www.jianshu.com/p/1484605c523a
  9. https://www.jianshu.com/p/43839abfdc1a
  10. https://stackoverflow.com/questions/55325062/how-to-serve-static-html-files-in-nest-js
  11. https://blog.csdn.net/weixin_43660626/article/details/102702461
  12. https://blog.csdn.net/weixin_34258782/article/details/94613224
  13. https://www.cnblogs.com/ajanuw/p/9574535.html
  14. https://blog.csdn.net/qq_27124771/article/details/81208493
  15. https://www.cnblogs.com/wolf-sun/p/6693367.html
  16. https://blog.csdn.net/weixin_41646716/article/details/90058189
  17. https://www.jianshu.com/p/5147e36cf19c
  18. https://blog.csdn.net/andywei147/article/details/80636539
  19. https://blog.csdn.net/lck8989/article/details/81021416
  20. https://www.jianshu.com/p/8fa0fb53c183

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章