Загрузка...

Broken picture

Thread in Frontend created by UnSad May 10, 2024. 207 views

  1. UnSad
    UnSad Topic starter May 10, 2024 21 Jun 1, 2019
    Хочу сделать загрузку аватарки через вебсокеты. Выбираю картинку, нажимаю загрузить, и вместо загружаемой картинки появляется битая пнг. После перезагрузки страницы, картинка появляется. Буду рад помощи :vinny:

    Нетворк таб(вместо username+filename, [object file]),:


    Бэк(golang / gin / gorilla websockets ):

    Code
    func UserAvatarWebsocket(c *gin.Context) {
    // Check origin error
    upgrader.CheckOrigin = func(r *http.Request) bool { return true }

    // Upgrade HTTP connection to WebSocket
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
    log.Println("Failed to set up WebSocket connection:", err)
    return
    }

    // Read incoming message from client
    _, msg, err := conn.ReadMessage()
    if err != nil {
    log.Println("Failed to read message from client:", err)
    return
    }

    // Get the authenticated user from context using type assertion
    user, exists := c.Get("user")
    if !exists {
    log.Println("Error: User not found in context")
    conn.WriteMessage(websocket.TextMessage, []byte("Unauthorized"))
    return
    }

    log.Printf("Type of user in context: %T\n", user)

    // Type assert the user to models.User
    currentUser, ok := user.(models.User)
    if !ok {
    log.Println("Error: Unexpected user type in context")
    conn.WriteMessage(websocket.TextMessage, []byte("Internal server error"))
    return
    }

    // Parse the message JSON to get the file name
    var filename struct {
    Filename string `json:"filename"`
    }

    if err := json.Unmarshal(msg, &filename); err != nil {
    log.Println("Failed to parse filename from message:", err)
    conn.WriteMessage(websocket.TextMessage, []byte("Failed to parse filename"))
    return
    }

    // Receive file from client
    _, file, err := conn.NextReader()
    if err != nil {
    log.Println("Failed to receive file from client:", err)
    conn.WriteMessage(websocket.TextMessage, []byte("Failed to receive file"))
    return
    }

    // Save the file to a location
    filePath := filepath.Join("uploads", currentUser.Username+"_"+filename.Filename)
    if err := saveFile(filePath, file); err != nil {
    log.Println("Failed to save file:", err)
    conn.WriteMessage(websocket.TextMessage, []byte("Failed to save file"))
    return
    }

    // Update the user's avatar in the database
    currentUser.Avatar = filePath
    if err := initializers.DB.Save(&currentUser).Error; err != nil {
    log.Println("Failed to update user in database:", err)
    conn.WriteMessage(websocket.TextMessage, []byte("Failed to update avatar"))
    return
    }

    // Send a response back to the client
    if err := conn.WriteMessage(websocket.TextMessage, []byte("Avatar updated successfully")); err != nil {
    log.Println("Failed to write message to client:", err)
    return
    }

    // Set cache headers
    c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
    c.Header("Pragma", "no-cache")
    c.Header("Expires", "0")

    defer conn.Close()
    }
    main.go:
    Code
    package main

    import (
    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
    "log"
    "server/controllers"
    "server/initializers"
    "server/middleware"
    "time"
    )

    func init() {
    initializers.LoadEnvVariables()
    initializers.ConnectToDb()
    initializers.SyncDatabase()
    }

    func main() {
    r := gin.Default()

    r.Static("/uploads", "./uploads")

    r.ForwardedByClientIP = true
    err := r.SetTrustedProxies([]string{"127.0.0.1"})
    if err != nil {
    return
    }

    r.Use(cors.New(cors.Config{
    AllowOrigins: []string{"http://localhost:8000", "http://localhost:5173"},
    AllowMethods: []string{"PUT", "PATCH", "DELETE", "GET", "POST", "OPTIONS"},
    AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
    ExposeHeaders: []string{"Content-Length"},
    AllowCredentials: true,
    MaxAge: 24 * time.Hour,
    }))

    r.POST("/signup", controllers.SignUp)
    r.POST("/login", controllers.Login)


    r.GET("/validate", middleware.RequireAuth, controllers.Validate)
    r.GET("/logout", middleware.RequireAuth, controllers.Logout)


    r.PUT("/update-password", middleware.RequireAuth, controllers.UpdatePassword)

    r.DELETE("/delete-user", middleware.RequireAuth, controllers.DeleteUser)

    ws := r.Group("/ws")
    {
    ws.Use(middleware.RequireAuth)
    ws.GET("/update-username", controllers.UpdateUsernameWebsocket)
    ws.GET("/upload-avatar", controllers.UserAvatarWebsocket)
    }

    log.Fatal(r.Run())

    }
    frontend(react ):

    JS
     const handleAvatarUpload = () => {
    if (!avatar) {
    toast.error("Please select an image to upload", {
    theme: "dark",
    autoClose: 3000,
    });
    return;
    }

    const ws = new WebSocket("ws://localhost:8000/ws/upload-avatar");

    ws.onopen = () => {
    try {
    // Send file metadata and data separately
    ws.send(JSON.stringify({ filename: avatar.name }));

    const fileReader = new FileReader();
    fileReader.readAsArrayBuffer(avatar);

    fileReader.onload = () => {
    const fileData = fileReader.result;
    if (fileData) {
    // Only send if fileData is not null
    ws.send(fileData);
    } else {
    // Handle the case where fileData is null
    toast.error("Failed to read file data", {
    theme: "dark",
    autoClose: 3000,
    });
    }
    };

    ws.onmessage = (event) => {
    const message = event.data.toString();

    if (message === "Avatar updated successfully") {
    onUpdateAvatar(avatar);
    setAvatar(null);
    toast.success("Avatar updated successfully", {
    theme: "dark",
    autoClose: 3000,
    });
    } else {
    console.log(message, event);
    }
    };
    } catch (error) {
    toast.error("Failed to update avatar", {
    theme: "dark",
    autoClose: 3000,
    });
    }
    };

    ws.onerror = function (error) {
    console.error("WebSocket error:", error);
    toast.error("WebSocket error", {
    theme: "dark",
    autoClose: 3000,
    });
    ws.close();
    };
    };
     
  2. azurescens
    azurescens May 10, 2024 5065 Dec 3, 2023
    Какой смысл загрузки файла через вебсокет если у тебя каждый раз новое соединение устанавливается? Можно для этого использоваться обычный http POST с таким же успехом
    The post was merged to previous May 10, 2024
    Да и в целом простые картинки лучше по http загружать, а вебсокет использовать если нужно стримить какие-то данные или большие объемы данных загружать на сервер
     
    1. UnSad Topic starter
      azurescens, В целом у меня так и было(загружал через http), просто я хотел написать это с использованием вебсокетов.
    2. azurescens
      UnSad, тогда тебе не нужно создавать каждый раз новое соединение при вызове функции, вебсокеты работают таким образом что соединение создается один раз и дальше остается открытым и уже внутри этого открытого соединения клиент и сервер обмениваются ивентами, в твоем же случае это работает как обычный http запрос. И тебе не нужен отдельный эндпоинт под каждую задачу в вебсокете, это не РЕСТ. Ты один раз подключаешься к https://ws://localhost:8000/ws и потом внутри этого соединения отправляешь ивенты "message", а на сервере своем эти ивенты обрабатываешь. В стандартной реализации 4 ивента доступно open close error и message, если хочешь создавать свои ивенты можешь присмотреться к https://socket.io/ библиотеке. Подробнее про вебсокеты тут почитать можешь https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
  3. azurescens
    azurescens May 10, 2024 5065 Dec 3, 2023
    И проблема с загрузкой файлов через вебсокет еще в том что с неправильной реализацией можешь заблокировать соединение на время загрузки файла, поэтому все должно происходить асинхронно для каждого клиента. Я бы на твоем месте просто использовал http метод для этого и не усложнял жизнь себе
     
    1. UnSad Topic starter
Loading...
Top