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.
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',
}
})