[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blog-translations-none":3,"academy-blogs-en-1-1-all-file-upload-websocket-chat-all--*":4},{},{"data":5,"meta":95},[6],{"categoryId":7,"collectionId":8,"collectionName":9,"content":10,"createBy":11,"createDate":12,"created":13,"description":14,"expand":15,"group":87,"id":87,"image":88,"imageAlt":89,"imagePath":90,"keywordIds":91,"langId":83,"publishDate":36,"scheduleDate":12,"slug":92,"status":28,"title":89,"updateBy":11,"updated":93,"views":94},"wqxt7ag2gn7xcmk","sclblg987654321","school_blog","\u003Ch3>Why Have a File Upload Feature in WebSocket Chat?\u003C\u002Fh3>\u003Cp>In modern chat systems, users want to share files, images, and documents with one another. Adding a file upload feature via WebSocket allows users to send files in real-time without reloading the page, making it ideal for:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cstrong>Internal Corporate Chats:\u003C\u002Fstrong> Where documents and work files need to be shared.\u003C\u002Fli>\u003Cli>\u003Cstrong>Social Media Platforms:\u003C\u002Fstrong> Allowing users to send images and videos.\u003C\u002Fli>\u003Cli>\u003Cstrong>Customer Support Services: \u003C\u002Fstrong>Where agents may need to send attachments or photos regarding various issues.\u003C\u002Fli>\u003C\u002Ful>\u003Ch3>Structure of the File Upload System in WebSocket Chat\u003C\u002Fh3>\u003Col>\u003Cli>\u003Cstrong>WebSocket Server: \u003C\u002Fstrong>Receives files from users and distributes them to relevant users.\u003C\u002Fli>\u003Cli>\u003Cstrong>GraphQL API:\u003C\u002Fstrong> Manages file uploads and stores metadata.\u003C\u002Fli>\u003Cli>\u003Cstrong>File Storage:\u003C\u002Fstrong> Stores files on a server or in Cloud Storage (e.g., AWS S3 or Firebase Storage).\u003C\u002Fli>\u003Cli>\u003Cstrong>Database (PostgreSQL \u002F MongoDB): \u003C\u002Fstrong>Stores file data, such as URLs and sender information.\u003C\u002Fli>\u003C\u002Fol>\u003Ch3>Install Necessary Libraries\u003C\u002Fh3>\u003Cpre>\u003Ccode class=\"language-plaintext\">go get github.com\u002Fgorilla\u002Fwebsocket\ngo get github.com\u002Fminio\u002Fminio-go\u002Fv7\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>Database Setup for File Data\u003C\u002Fh3>\u003Cp>File: \u003Ccode>\u003Cspan>schema.sql\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">CREATE TABLE chat_files (\n    id SERIAL PRIMARY KEY,\n    room_id INTEGER NOT NULL,\n    sender TEXT NOT NULL,\n    file_url TEXT NOT NULL,\n    file_name TEXT NOT NULL,\n    file_type TEXT NOT NULL,\n    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>Creating GraphQL Schema for File Upload\u003C\u002Fh3>\u003Cp>File: \u003Ccode>\u003Cspan>schema.graphql\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">type Mutation {\n  uploadFile(roomID: ID!, sender: String!, fileName: String!, fileType: String!, fileContent: String!): ChatFile!\n}\n\ntype ChatFile {\n  id: ID!\n  roomID: ID!\n  sender: String!\n  fileURL: String!\n  fileName: String!\n  fileType: String!\n  timestamp: String!\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>Creating Resolver for File Upload\u003C\u002Fh3>\u003Cp>File: \u003Ccode>\u003Cspan>resolver.go\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">package main\n\nimport (\n    \"context\"\n    \"encoding\u002Fbase64\"\n    \"fmt\"\n    \"os\"\n    \"path\u002Ffilepath\"\n    \"time\"\n)\n\ntype ChatFile struct {\n    ID        int       `json:\"id\"`\n    RoomID    int       `json:\"roomID\"`\n    Sender    string    `json:\"sender\"`\n    FileURL   string    `json:\"fileURL\"`\n    FileName  string    `json:\"fileName\"`\n    FileType  string    `json:\"fileType\"`\n    Timestamp time.Time `json:\"timestamp\"`\n}\n\nfunc (r *Resolver) Mutation_uploadFile(ctx context.Context, roomID int, sender string, fileName string, fileType string, fileContent string) (ChatFile, error) {\n    decoded, err := base64.StdEncoding.DecodeString(fileContent)\n    if err != nil {\n        return ChatFile{}, err\n    }\n\n    savePath := filepath.Join(\"uploads\", fileName)\n    err = os.WriteFile(savePath, decoded, 0644)\n    if err != nil {\n        return ChatFile{}, err\n    }\n\n    fileURL := fmt.Sprintf(\"\u002Fuploads\u002F%s\", fileName)\n    file := ChatFile{RoomID: roomID, Sender: sender, FileURL: fileURL, FileName: fileName, FileType: fileType, Timestamp: time.Now()}\n    return file, nil\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>Sending Files via WebSocket\u003C\u002Fh3>\u003Cp>File: \u003Ccode>\u003Cspan>websocket_server.go\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">package main\n\nimport (\n    \"encoding\u002Fjson\"\n    \"fmt\"\n    \"net\u002Fhttp\"\n    \"github.com\u002Fgorilla\u002Fwebsocket\"\n)\n\ntype FileMessage struct {\n    RoomID    int    `json:\"roomID\"`\n    Sender    string `json:\"sender\"`\n    FileName  string `json:\"fileName\"`\n    FileType  string `json:\"fileType\"`\n    FileURL   string `json:\"fileURL\"`\n}\n\nvar upgrader = websocket.Upgrader{\n    CheckOrigin: func(r *http.Request) bool { return true },\n}\n\nfunc handleWebSocket(w http.ResponseWriter, r *http.Request) {\n    conn, _ := upgrader.Upgrade(w, r, nil)\n    defer conn.Close()\n    fmt.Println(\"Client connected\")\n\n    for {\n        _, msg, err := conn.ReadMessage()\n        if err != nil {\n            break\n        }\n        var fileMsg FileMessage\n        json.Unmarshal(msg, &amp;fileMsg)\n        fmt.Printf(\"Received file: %s from %s\\n\", fileMsg.FileName, fileMsg.Sender)\n    }\n}\n\nfunc main() {\n    http.HandleFunc(\"\u002Fws\", handleWebSocket)\n    fmt.Println(\"WebSocket Server Running on Port 8080\")\n    http.ListenAndServe(\":8080\", nil)\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>Uploading Files from the Client Side\u003C\u002Fh3>\u003Cp>File: \u003Ccode>\u003Cspan>client.js\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-plaintext\">async function uploadFile(file) {\n    const reader = new FileReader();\n    reader.readAsDataURL(file);\n    reader.onload = async () =&gt; {\n        const base64Content = reader.result.split(\",\")[1];\n        const response = await fetch(\"\u002Fgraphql\", {\n            method: \"POST\",\n            headers: { \"Content-Type\": \"application\u002Fjson\" },\n            body: JSON.stringify({\n                query: `mutation { uploadFile(roomID: 1, sender: \"JohnDoe\", fileName: \"${file.name}\", fileType: \"${file.type}\", fileContent: \"${base64Content}\" ) { fileURL } }`\n            })\n        });\n        console.log(await response.json());\n    };\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>Challenge!\u003C\u002Fh3>\u003Cp>Try adding a file preview feature before upload, allowing users to review the file before sending it to the chat room.\u003C\u002Fp>\u003Chr>\u003Ch3>Next EP\u003C\u002Fh3>\u003Cp>In EP.41, we will add a typing indicator feature in the WebSocket Chat! 🚀\u003C\u002Fp>","r8v4zgsahjuwpeb","","2026-03-04 08:51:10.970Z","Learn how to implement a file upload feature in WebSocket Chat using Go and GraphQL, supporting file sharing via WebSocket and storing files on a server or cloud storage systems like AWS S3 or Firebase Storage.",{"categoryId":16,"keywordIds":30,"langId":78},{"blogIds":17,"collectionId":18,"collectionName":19,"createBy":20,"created":21,"id":7,"image":22,"imageAlt":12,"imagePath":23,"label":24,"name":25,"priority":26,"publishDate":27,"scheduleDate":12,"status":28,"updateBy":20,"updated":29},[],"sclcatblg987654321","school_category_blog","oplnwslvnmx5axc","2026-03-04 08:33:53.210Z","59ty92ns80w_15oc1implw.png","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Fwqxt7ag2gn7xcmk\u002F59ty92ns80w_15oc1implw.png",{"en":25,"th":25},"Golang The Series",1,"2026-03-16 04:39:38.440Z","Publish","2026-03-17 06:07:59.733Z",[31,38,42,46,50,55,60,65,69,74],{"collectionId":32,"collectionName":33,"createBy":12,"created":34,"id":35,"publishDate":36,"scheduleDate":12,"status":28,"title":37,"updateBy":12,"updated":34},"sclkey987654321","school_keyword","2026-03-04 08:51:07.889Z","svzsiusj88bni77","2025-03-17 02:13:28.952Z","Chat Media Upload",{"collectionId":32,"collectionName":33,"createBy":12,"created":39,"id":40,"publishDate":36,"scheduleDate":12,"status":28,"title":41,"updateBy":12,"updated":39},"2026-03-04 08:51:08.283Z","ux24tskiyudefze","Firebase Storage",{"collectionId":32,"collectionName":33,"createBy":12,"created":43,"id":44,"publishDate":36,"scheduleDate":12,"status":28,"title":45,"updateBy":12,"updated":43},"2026-03-04 08:51:08.494Z","bqukhjhwr4yxnts","AWS S3",{"collectionId":32,"collectionName":33,"createBy":12,"created":47,"id":48,"publishDate":36,"scheduleDate":12,"status":28,"title":49,"updateBy":12,"updated":47},"2026-03-04 08:51:08.805Z","75w01ebjq7zfrpt","GraphQL File Upload",{"collectionId":32,"collectionName":33,"createBy":12,"created":51,"id":52,"publishDate":53,"scheduleDate":12,"status":28,"title":54,"updateBy":12,"updated":51},"2026-03-04 08:51:09.476Z","l1ipjxpi9rr2jgh","2025-03-17 02:13:24.978Z","WebSocket File Sharing",{"collectionId":32,"collectionName":33,"createBy":12,"created":56,"id":57,"publishDate":58,"scheduleDate":12,"status":28,"title":59,"updateBy":12,"updated":56},"2026-03-04 08:47:05.949Z","caufix9o52uw4bh","2025-08-18 13:50:26.690Z","Real-Time Chat",{"collectionId":32,"collectionName":33,"createBy":12,"created":61,"id":62,"publishDate":63,"scheduleDate":12,"status":28,"title":64,"updateBy":12,"updated":61},"2026-03-04 08:20:14.253Z","ah6lvy4x8qe08l5","2026-01-28 00:54:28.566Z","Golang",{"collectionId":32,"collectionName":33,"createBy":12,"created":66,"id":67,"publishDate":63,"scheduleDate":12,"status":28,"title":68,"updateBy":12,"updated":66},"2026-03-04 08:20:11.547Z","ey3puyme01a9bsw","Go",{"collectionId":32,"collectionName":33,"createBy":12,"created":70,"id":71,"publishDate":72,"scheduleDate":12,"status":28,"title":73,"updateBy":12,"updated":70},"2026-03-04 08:34:00.920Z","ecac9y661or1xka","2025-01-27 04:42:34.661Z","WebSocket",{"collectionId":32,"collectionName":33,"createBy":12,"created":75,"id":76,"publishDate":36,"scheduleDate":12,"status":28,"title":77,"updateBy":12,"updated":75},"2026-03-04 08:51:09.841Z","6hrhrxemlcwn5fx","File Upload",{"code":79,"collectionId":80,"collectionName":81,"createAt":82,"id":83,"is_default":84,"language":85,"updateAt":86},"en","pbc_1989393366","locale","2026-01-22 11:00:02.726Z","qv9c1llfov2d88z",false,"English","2026-02-05 10:48:59.032Z","eapi26ab5gp36fo","52_11zon_7xeblwj17w.webp","EP.40 Adding File Upload Feature in WebSocket Chat","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002Feapi26ab5gp36fo\u002F52_11zon_7xeblwj17w.webp",[35,40,44,48,52,57,62,67,71,76],"file-upload-websocket-chat","2026-03-04 08:51:11.479Z",219,{"pagination":96},{"page":26,"pageSize":26,"pageCount":26,"total":26}]