Загрузка файла в проект React Native Expo не работает

В ответ от серверной части я получаю «Файл не загружен».

import ImagePicker from "expo-image-picker";
export const UpdateProfile = () => {
  const [address, setAddress] = useState("");
  const [formPostImg, setFormPostImg] = useState("");

  const [errMsg, setErrMsg] = useState<Record<string, string>>({
    profile: "",
  });

  const openImageLibraryKeyMessage = async () => {
    const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
    if (status !== "granted") {
      alert("Sorry, we need camera roll permissions to make this work!");
    }

    if (status === "granted") {
      const result = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.All,
        allowsEditing: true,
      });

      if (!result.canceled) {
        setFormPostImg(result.assets[0].uri);
        console.info({ formPostImg });
      }
    }
  };

  const handleUpdateProfile = async () => {
    console.info("clicked");
    console.info({ formPostImg });

    const formBody = new FormData();
    formBody.append("address", address);
    formBody.append("img", formPostImg);
    try {
      const res = await updateProfileFn({
        formBody,
        setErrMsg,
      });
      if (res && res.message.includes("success")) {
        console.info("success");
      }
    } catch (error) {
      // disable empty object error
    }
  };

  return (
    <SafeAreaView style = {styles.mainBox}>
      {errMsg.profile && <Text>{errMsg.profile}</Text>}
      <View>
        <Text>Address</Text>
        <TextInput
          onChangeText = {setAddress}
          value = {address}
          placeholder = "Enter address"
          style = {styles.textInput}
        />
      </View>

      <Pressable onPress = {openImageLibraryKeyMessage}>
        <Text>Pick image</Text>
      </Pressable>

      <Pressable onPress = {handleUpdateProfile}>
        <Text>Update</Text>
      </Pressable>
    </SafeAreaView>
  );
};

Я console.info({ formPostImg }); и получите ответ, похожий на

LOG  {"formPostImg": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Ffrontend-a5b88748-c8d6-412d-b8cc-ee88decfab14/ImagePicker/72bb7e65-9d16-4a78-a14e-009cd8cca53c.jpeg"}

который показывает, что изображение сохраняется в состоянии, однако, когда я пытаюсь загрузить, я получаю сообщение об ошибке «Файл не загружен» из серверной части

Это функция, которую я вызываю

async function updateProfileFn({
  formBody,
  setErrMsg,
}: {
  formBody: FormData;
  setErrMsg: React.Dispatch<React.SetStateAction<Record<string, string>>>;
}): Promise<ResSuccess | undefined> {
  const url = `${BASE_URL}/api/v1/users/updateProfile`;

  const token = await SecureStore.getItemAsync("token");
  console.info({ formBody });

  const res = await fetch(url, {
    method: "PATCH",
    headers: {
      "Content-Type": "multipart/form-data",
      Accept: "application/json",
      Authorization: `Bearer ${token}`,
    },
    body: formBody,
  });

  if (!res.ok) {
    const exactErrorMsg = await res.json();
    const errorMsgString = JSON.stringify(exactErrorMsg);
    const errorMsg = JSON.parse(errorMsgString).error;

    // Set the error message in the state
    setErrMsg((prev) => ({ ...prev, profile: errorMsg }));

    // Throw an error to stop further execution
    return;
  }

  const data: ResSuccess = await res.json();

  console.info({ data });

  return data;
}
export default updateProfileFn;

Это внутренний код для большего контекста

export const updateProfile: express.RequestHandler = (
  req: Request,
  res: Response
) => {
  const { id: user_id } = getUserIDAndToken(req);

  if (!user_id) {
    res.status(401).json({ error: "Unauthorized" });
    return;
  }

  upload.single("img")(req, res, async (err) => {
    if (err) {
      if (err.message === "Unexpected field") {
        // console.info(error.message);

        res.status(400).json({ error: "Maximum of 1 image allowed" });
        return;
      }
    }

    const {
      address,
      phone_number,
      account_name,
      email,
      tag,
    }: {
      address: string;
      phone_number: string;
      account_name: string;
      email: string;
      tag: string;
    } = req.body;
    console.info({ address, phone_number, account_name, email, tag });

    // create uniqueIdentifier for the image
    const uniqueIdentifier = Date.now() + "-" + Math.round(Math.random() * 1e9);

    // create publicId for the image for cloudinary
    const publicId = `profile-img-${uniqueIdentifier}`;

    console.info(req.files);
    console.info(req.file?.path);

    if (!req.file) {
      res.status(400).json({ error: "No file uploaded" });
      return;
    }

    if (!address || !phone_number || !account_name || !email) {
      res.status(400).json({ error: "Missing required fields" });
      return;
    }

    try {
      const result = await cloudinary.uploader.upload(req.file.path, {
        public_id: publicId,
      });
      const imgURL = result.secure_url;
      console.info("imgURL", imgURL);

      pool.execute<ResultSetHeader>(
        `
                UPDATE users
                SET img = ?, address = ?, phone_number = ?, account_name = ?, email = ?, tag = ?
                WHERE id = ?
                `,
        [imgURL, address, phone_number, account_name, email, tag, user_id],
        (err) => {
          if (err) {
            console.error(err);
            res.status(500).json({ error: "Internal server error" });
            return;
          } else {
            console.info({ result });
            res.status(200).json({ message: "Profile updated successfully" });
          }
        }
      );
    } catch (error) {
      console.error(error);
      res.status(500).json({ error: "Internal server error" });
    }
  });
};

Журнал этих

console.info(req.files);
console.info(req.file?.path);

оба не определены

Что вы используете в качестве API для определения маршрута?

Devon Ray 07.05.2024 15:18

Это мой импорт маршрута {Маршрутизатор} из "экспресса"; константный маршрутизатор = Маршрутизатор(); импортировать { updateProfile } из «../controllers/users/profile/update»; router.route("/updateProfile").patch(updateProfile); экспортировать маршрутизатор по умолчанию; А это мой index.ts import userRouter из "./routes/users"; app.use("/api/v1/users", userRouter);

coderx 07.05.2024 15:21
Умерло ли Create-React-App?
Умерло ли Create-React-App?
В этом документальном фильме React.dev мы исследуем, мертв ли Create React App (CRA) и какое будущее ждет этот популярный фреймворк React.
Освоение React Native: Пошаговое руководство для начинающих
Освоение React Native: Пошаговое руководство для начинающих
React Native - это популярный фреймворк с открытым исходным кодом, используемый для разработки мобильных приложений. Он был разработан компанией...
В чем разница между react native и react ?
В чем разница между react native и react ?
React и React Native - два популярных фреймворка для создания пользовательских интерфейсов, но они предназначены для разных платформ. React - это...
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
Если вы уже умеете работать с React, создание мобильных приложений для iOS и Android - это новое приключение, в котором вы сможете применить свои...
Хуки React: что это такое и как их использовать
Хуки React: что это такое и как их использовать
Хуки React - это мощная функция библиотеки React, которая позволяет разработчикам использовать состояние и другие возможности React без написания...
1
2
107
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, что ваша проблема может быть в ваших formData. Я делаю это, чтобы загрузить изображение, используя данные формы в ответе Native:

      formData.append(
        `${image.name}`,      <=== Name of upload
        {
          uri: image.uri,
          name: image.name,
          type: 'image/jpeg',
        } as any,                    <== Blob
        `${image.name}`       <=== FileName
      );

Это то, что сработало для меня, так что, надеюсь, это и ваша проблема!

Вот ошибка, которую я получил с вашим методом. o перегрузка соответствует этому вызову. Перегрузка 1 из 3, «(имя: строка, значение: строка | Blob): void» выдала следующую ошибку. Литерал объекта может указывать только известные свойства, а «uri» не существует в типе «Blob». Перегрузка 2 из 3, «(имя: строка, значение: строка): void», выдала следующую ошибку. Аргумент типа '{ type: string; ури: строка; имя: строка; }» нельзя назначить параметру типа «строка». Перегрузка 3 из 3, «(имя: строка, blobValue: Blob, имя файла?: строка | неопределенное): void» выдала следующую ошибку...

coderx 07.05.2024 15:42

На этой линии. formBody.append("img", {type: "image/jpeg", uri: formPostImg, name: 'file.jpg'});

coderx 07.05.2024 15:43

Так это сработало для вас? Или у вас все еще есть проблемы?

Dustin Forsyth 07.05.2024 16:04

Читая ваш комментарий выше, если вы используете машинописный текст, у меня тоже были проблемы с типами, и я просто использовал blob как любой, потому что не мог исправить типы, возможно, кто-то умнее меня может объяснить несовпадение типов. Я отредактирую свой ответ, чтобы он соответствовал любому.

Dustin Forsyth 07.05.2024 16:09

Все работает, ошибка машинописи. В остальном загрузка работает как положено. Спасибо.

coderx 07.05.2024 16:10

Чтобы удалить ошибку машинописного текста и если eslint выдаст предупреждение при использовании любого значения, вы можете использовать неизвестное значение, например blob. Так. formBody.append("img", { type: "image/jpeg", uri: formPostImg, name: "file.jpg", } так же неизвестно, как Blob);

coderx 07.05.2024 16:14

Другие вопросы по теме

Как правильно обрабатывать ошибки в функции транзакцииAsync expo-sqlite?
`tsc` продолжает отслеживать `node_modules`. Может ли кто-нибудь помочь мне решить эту проблему?
Ошибка в моем собственном коде реагирования с использованием обработчика жестов
Собственные данные React expo sqlite/next не отображаются в файле БД, пока файл открыт в браузере БД
Сбой приложения Expo при открытии с исключением в HostObject::get для реквизита «Качества»:
Отправка данных аудиозаписи в реальном времени через сокет
При открытии выставки на iOS происходит сбой с ошибкой: выход из xcrun с ненулевым кодом: 60 (домен = NSPOSIXErrorDomain, код = 60)
Обновить WebView до начального URL-адреса после переключения вкладок
Как перестать отображать новый файл в виде списка меню в маршрутизаторе Expo?
Ошибка: исключение в HostObject::get для prop > «NativeUnimoduleProxy» — происходит сбой виртуального устройства Android (AVD) при использовании Expo для тестирования приложения React Native