Search code examples
react-nativeapirestdjango-rest-frameworkexpo

Expo React Native post method not including MEDIA on Django rest framework right path


I'm trying to create an Expo React Native app where I send a Pdf File to my API created in Django Rest Framework. But, when I send the pdf file, it is not being save on the right place, I'll put the code and logs to explain better.

# models.py

from django.db import models


# Create your models here.
class PDFFile(models.Model):
  name = models.CharField(max_length=200, blank=True)
  pdf_file = models.FileField(upload_to="pdfs/", blank=True)

  def __str__(self):
    return str(self.name)
# serializers.py
from rest_framework import serializers
from .models import PDFFile


class PDFFileModelSerializer(serializers.ModelSerializer):
  class Meta:
    model = PDFFile
    fields = ["id", "name", "pdf_file"]
# views.py
from .models import PDFFile
from .serializers import PDFFileModelSerializer

from rest_framework import viewsets, status
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response

class PDFFileViewSet(viewsets.ModelViewSet):
  queryset = PDFFile.objects.all()
  serializer_class = PDFFileModelSerializer
  parser_classes = (MultiPartParser, FormParser)

  def create(self, request, *args, **kwargs):
    name = request.data["name"]
    pdf_file = request.data["pdf_file"]
    print("PDF FILE: ", pdf_file)

    PDFFile.objects.create(name=name, pdf_file=pdf_file)

    return Response("PDF create with success!", status=status.HTTP_200_OK)
  
# urls.py from the app

from rest_framework import routers
from .views import PDFFileViewSet
from django.urls import path, include

router = routers.DefaultRouter()
router.register("pdfs", PDFFileViewSet)

urlpatterns = [
  path("", include(router.urls))
]

# urls.py from the project
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include("pdf_uploader.urls")),
]  + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


# settings.py

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
     )
 }

DATA_UPLOAD_MAX_MEMORY_SIZE = 5242880

STATIC_URL = '/static/'

MEDIA_URL = '/media/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

STATIC_ROOT = BASE_DIR / 'staticfiles'

Expo React Native component:

import { View, Text, StyleSheet, TouchableOpacity, SafeAreaView, Alert } from 'react-native'
import React, { useState } from 'react'
import * as DocumentPicker from 'expo-document-picker'
import { PDF_BASE_URL } from '../data/api'

const PdfPicker = () => {
  
  const [pdfFile, setPdfFile] = useState(null)
  const [name, setName] = useState("")
  const [uploading, setUploading] = useState(false)
  
  const pickerPdf = async () => {
    let result = await DocumentPicker.getDocumentAsync({
      type: 'application/pdf',
    })
    
    const fileName = result.uri.split('/').pop() // extract the file name from the URI
    const source = { uri: fileName, name: result.name }
    console.log("source", source)
    setPdfFile(source.uri)
    setName(source.name)
  }
  
  const uploadPdfFile = async () => {
    setUploading(true)
    
    const uploadPdf = pdfFile
    const pdfName = name
    const data = new FormData()

    data.append("name", pdfName)
    data.append("pdf_file", uploadPdf)

    const response = await fetch(PDF_BASE_URL + "pdfs/", {
      method: "POST",
      headers: {
        "Content-Type": "multipart/form-data",
      },
      body: data
    })

    console.log("RESULT: ", JSON.stringify(response))

    try {
      await JSON.stringify(response)
    } catch (e) {
      console.log("Catch error:", e)
    }
    setUploading(false)
    Alert.alert(
      "Pdf file uploaded..!!"
    )
    setName("")
    setPdfFile(null)
  }

  return (
    <SafeAreaView style={styles.container} >
      <View>
        <Text style={{color: "white", marginBottom: 30}}>{name}</Text>
      </View>
      <TouchableOpacity style={styles.selectButton} onPress={pickerPdf} >
        <Text style={styles.buttonText}>Pick a Pdf file</Text>
      </TouchableOpacity>
      <View style={styles.pdfContainer}>
        <TouchableOpacity style={styles.uploadButton} onPress={uploadPdfFile}>
          <Text style={styles.buttonText}>Upload Pdf file</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  )
}

export default PdfPicker

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    backgroundColor: '#000',
    justifyContent: 'center',
  },
  selectButton: {
    borderRadius: 5,
    width: 150,
    height: 50,
    backgroundColor: 'blue',
    alignItems: 'center',
    justifyContent: 'center',
  },
  buttonText: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold'
  },
  pdfContainer: {
    marginTop: 30,
    marginBottom: 50,
    alignItems: 'center',
  },
  uploadButton: {
    borderRadius: 5,
    width: 150,
    height: 50,
    backgroundColor: 'green',
    alignItems: 'center',
    justifyContent: 'center',
  }
})

The Expo log

 LOG  RESULT:  {"type":"default","status":200,"ok":true,"statusText":"","headers":{"map":{"allow":"GET, POST, HEAD, OPTIONS","content-length":"26","content-type":"application/json","cross-origin-opener-policy":"same-origin","date":"Thu, 06 Apr 2023 14:19:30 GMT","referrer-policy":"same-origin","server":"railway","vary":"Accept, Origin, Cookie","x-content-type-options":"nosniff","x-frame-options":"DENY"}},"url":"{my.server}/api/pdfs/","bodyUsed":false,"_bodyInit":{"_data":{"size":26,"offset":0,"blobId":"837A79CD-3B4F-4D1C-A15D-74BE46D9CD26","type":"application/json","name":"pdfs.json","__collector":{}}},"_bodyBlob":{"_data":{"size":26,"offset":0,"blobId":"837A79CD-3B4F-4D1C-A15D-74BE46D9CD26","type":"application/json","name":"pdfs.json","__collector":{}}}}

Sent by the browser:

{
        "id": 5,
        "name": "livro",
        "pdf_file": "{my.server}/media/pdfs/Stephen.King.-.A.Balsa_WF9W8Kc.pdf"
    }

Sent by Expo app

{
        "id": 4,
        "name": "CERTIDAO-IANDERSONPIZETTEUENDERSONPABLOIII.pdf",
        "pdf_file": "{my.server}/media/C2699748-A1CF-49F5-A3C7-6B527AD3FF44.pdf"
    },

Backend log:

Not Found: /media/C2699748-A1CF-49F5-A3C7-6B527AD3FF44.pdf

So, I can't see where is my problem here.

Maybe a new pair of eyes can help me.

I had read Django and DRF documentation, Expo React Native documentation. I tried get help from ChatGPT.

Now I'm trying get some help here.


Solution

  • I got the solution for my problem, I notice that what I was sending was the blobId of my pdf file by the mobile app, and by the browser,I was sending the downloaded pdf file, so I start to download the file on mobile app and then send to my backend.

    and worked. Here the code. I'll share the my solution for those who come on this post with same or close problem like mine.

    import { View, Text, StyleSheet, TouchableOpacity, SafeAreaView, Alert } from 'react-native'
    import React, { useState } from 'react'
    import * as DocumentPicker from 'expo-document-picker'
    import { PDF_BASE_URL } from '../data/api'
    import * as FileSystem from 'expo-file-system';
    
    const PdfTry = () => {
      const [pdfFileUri, setPdfFileUri] = useState(null)
      const [pdfFileName, setPdfFileName] = useState("")
      const [uploading, setUploading] = useState(false)
      
      const pickerPdf = async () => {
        let result = await DocumentPicker.getDocumentAsync({
          type: 'application/pdf',
          copyToCacheDirectory: false
        })
        
        const pdfUri = result.uri
        const pdfName = result.name
    
        // Download the selected PDF file to the device's local storage
        const fileUri = FileSystem.documentDirectory + pdfName
        await FileSystem.copyAsync({ from: pdfUri, to: fileUri })
        
        setPdfFileUri(fileUri)
        setPdfFileName(pdfName)
      }
      
      const uploadPdfFile = async () => {
        if (!pdfFileUri) {
          Alert.alert("Please select a PDF file first.")
          return
        }
    
        setUploading(true)
    
        const data = new FormData()
        data.append("name", pdfFileName)
        data.append("pdf_file", { uri: pdfFileUri, name: pdfFileName, type: 'application/pdf' })
    
        const response = await fetch(PDF_BASE_URL + "pdf_upload/", {
          method: "POST",
          headers: {
            "Content-Type": "multipart/form-data",
          },
          body: data
        })
    
        try {
          await JSON.stringify(response)
        } catch (e) {
          console.log("Catch error:", e)
        }
        setUploading(false)
        Alert.alert(
          "Pdf file uploaded..!!"
        )
        setPdfFileName("")
        setPdfFileUri(null)
      }
    
      return (
        <SafeAreaView style={styles.container} >
          <View>
            <Text style={{color: "white", marginBottom: 30}}>{pdfFileName}</Text>
          </View>
          <TouchableOpacity style={styles.selectButton} onPress={pickerPdf} >
            <Text style={styles.buttonText}>Pick a Pdf file</Text>
          </TouchableOpacity>
          <View style={styles.pdfContainer}>
            <TouchableOpacity style={styles.uploadButton} onPress={uploadPdfFile}>
              <Text style={styles.buttonText}>Upload Pdf file</Text>
            </TouchableOpacity>
          </View>
        </SafeAreaView>
      )
    }
    
    export default PdfTry
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#000',
        justifyContent: 'center',
      },
      selectButton: {
        borderRadius: 5,
        width: 150,
        height: 50,
        backgroundColor: 'blue',
        alignItems: 'center',
        justifyContent: 'center',
      },
      buttonText: {
        color: 'white',
        fontSize: 18,
        fontWeight: 'bold'
      },
      pdfContainer: {
        marginTop: 30,
        marginBottom: 50,
        alignItems: 'center',
      },
      uploadButton: {
        borderRadius: 5,
        width: 150,
        height: 50,
        backgroundColor: 'green',
        alignItems: 'center',
        justifyContent: 'center',
      }
    })