Загрузка...

Битая картинка

Тема в разделе Frontend создана пользователем UnSad 10 май 2024. 203 просмотра

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

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


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

    Код
    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:
    Код
    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 10 май 2024 5065 3 дек 2023
    Какой смысл загрузки файла через вебсокет если у тебя каждый раз новое соединение устанавливается? Можно для этого использоваться обычный http POST с таким же успехом
    --- Сообщение объединено с предыдущим 10 май 2024
    Да и в целом простые картинки лучше по http загружать, а вебсокет использовать если нужно стримить какие-то данные или большие объемы данных загружать на сервер
     
    1. UnSad Автор темы
      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
      10 май 2024 Изменено
  3. azurescens
    azurescens 10 май 2024 5065 3 дек 2023
    И проблема с загрузкой файлов через вебсокет еще в том что с неправильной реализацией можешь заблокировать соединение на время загрузки файла, поэтому все должно происходить асинхронно для каждого клиента. Я бы на твоем месте просто использовал http метод для этого и не усложнял жизнь себе
     
    10 май 2024 Изменено
    1. UnSad Автор темы
Загрузка...
Top