How to load a PDF passed by Django to Angular? - django

In Django I have this function that returns a pdf to me
def get(self, request):
with open(filepath, 'rb') as f:
pdf = f.read()
return HttpResponse(pdf, content_type='application/pdf')
In Angular i have
this.http.get(apiUrl, { responseType: 'arraybuffer' }).subscribe(
response => {
// Create a blob object that represents the PDF file
var blob = new Blob([response], { type: 'application/pdf' });
// Create an object URL that points to the blob
var objectURL = URL.createObjectURL(blob);
// Use the object URL as the src attribute for an <iframe> element
// to display the PDF file
document.getElementById('iframe').src = objectURL;
},
error => {
console.log(error)
}
);
In the <iframe> I get "Unable to upload PDF document"
What am i doing wrong?

Related

Downloading a file via Axios from Django

I'm trying to get filename from blob request using Axios and Django.
Django's view:
with open(full_file_path, 'rb') as f:
response = HttpResponse(f, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
file_expr = "filename*=utf-8''{}".format(quote(file_name_with_ext))
response['Content-Disposition'] = 'attachment; {}'.format(file_expr)
return response
Frontend:
axios
.request({
url,
method,
responseType: 'blob',
})
.then(({ data }) => {
console.log(data);
const downloadUrl = window.URL.createObjectURL(new Blob([data]));
const link = document.createElement('a');
link.href = downloadUrl;
link.setAttribute('download', 'file.xlsx');
document.body.appendChild(link);
link.click();
link.remove();
});
But parameter 'data' doesn't contain headers to allow me to get filename.
Is there any solution?
instead of this .then(({ data }) => { Use this:.then(response =>{
and response.data is your data from response that you can see with console.log(response.data);
Hope it helps<3.

django rest framework doesn't accept blob picture file (File extension “” is not allowed)

I am trying to update a User Profile by making a multipart/form-data put request (from my vue frontend using axios) containing a png blob image file. I receive an error message: File extension “” is not allowed.
This is the File Field on the Userprofile Model:
profile_picture = models.FileField(
_("Profile Pictures"),
upload_to="profile_picture",
max_length=100,
blank=True,
null=True,
)
These are the signals I use in the Userprofile model to save and update the Model.
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
I think it may be because of these or something other specific to the Userprofile model that creates the error because on another model the file upload works as intended, although there I am making a post and not a put request. The Userprofile model is connected to the User by a One to One Field.
Ther is nothing special in my serializer or views that could be causing the bug.
I have no Idea what I could do to fix this. Thanks for all advice. If you need any other information feel free to ask.
The image is the formdata sent with the put request... maybe somethings wrong there:
The axios code that makes the request:
import axios from 'axios'
const apiClient = axios.create({
baseURL: `http://127.0.0.1:8000/`,
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data'
}
})
export default {
updateUser(pk, params) {
return apiClient.put('/users/' + pk + '/', params)
}
}
This is the part where I crop a picture and it is a dataURI which I then convert to a blob to send to the backend server:
methods: {
crop() {
const { coordinates, canvas } = this.$refs.cropper.getResult()
this.coordinates = coordinates
this.file = canvas.toDataURL()
},
uploadImage(event) {
const input = event.target
if (input.files && input.files[0]) {
// create a new FileReader to read this image and convert to base64 format
const reader = new FileReader()
// Define a callback function to run, when FileReader finishes its job
reader.onload = (e) => {
// Read image as base64 and set to imageData
this.file = e.target.result
}
// Start the reader job - read file as a data url (base64 format)
reader.readAsDataURL(input.files[0])
}
},
dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
const byteString = atob(dataURI.split(',')[1])
// separate out the mime component
const mimeString = dataURI
.split(',')[0]
.split(':')[1]
.split(';')[0]
// write the bytes of the string to an ArrayBuffer
const ab = new ArrayBuffer(byteString.length)
const ia = new Uint8Array(ab)
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i)
}
return new Blob([ab], { type: mimeString })
},
async updateUser() {
this.crop()
delete this.user.password2
const formData = new FormData()
if (this.file)
formData.append('profile_picture', this.dataURItoBlob(this.file))
if (this.user.username) formData.append('username', this.user.username)
else formData.append('username', this.$auth.user.username)
if (this.user.email) formData.append('email', this.user.email)
if (this.user.bio) formData.append('bio', this.user.bio)
if (this.user.password) formData.append('password', this.user.password)
else formData.append('password', this.$auth.user.password)
await UserFormService.updateUser(this.$auth.user.pk, formData)
await this.$store.dispatch('users/updateUser', this.user)
this.$auth.setUser(this.$store.state.users.user)
this.$router.push('/users/me')
Since your endpoint requires a specific file name extension you will have to set it manually because blobs do not have file names and the default used for a file upload is blob. There is a third optional parameter in FormData.append to set a file name
formData.append('profile_picture', this.dataURItoBlob(this.file), 'some_filename.valid_extension')

Django + Axios: File download not working in Firefox

I am using Axios to send my Django backend information, which in turns creates a file and sends it back to the front end. The code I have below works great in Safari and Chrome. However, the file does not download in firefox. BTW, no errors show up in the firefox console, or in Django.
Axios
axios({
method:'post',
url:'/api/downloadDoc',
responseType:'blob',
data: params,
})
.then(response => {
let blob = new Blob([response.data], {type: 'application/force-download'})
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = "YourPaper.docx"
link.click()
})
.catch(e => {
console.log(e)
})
Django View Sending File
def downloadPaper(request):
if request.method == "POST":
#Recieve info through post and create document
#variable path is path to newly created document
wrapper = FileWrapper(open(path, 'rb'))
response = HttpResponse(wrapper, content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
response['Content-Disposition'] = 'attachment; filename=' + 'yourpaper.docx'
response['Content-Length'] = os.path.getsize(path)
return response
return HttpResponse(status=405)
I am hoping someone knows what could be causing firefox to not download.
Thanks!
fetch("/api/admin/plugin/download/" + id , {
method: 'GET',
headers: new Headers({
"Authorization": this.token,
"Allow-Control-Allow-Origin": "*",
"Content-Type" : "application/octet-stream"
}),
}).then(response => {
response.blob().then(blob => {
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
// must add <a> to body, then it works
document.body.appendChild(a);
a.style.display = 'none';
a.href = url;
a.download = decodeURI(response.headers.get("filename"));
a.click();
document.body.removeChild(a);
});
})

ionic 2 upload image to django rest

I am trying to upload an image from Ionic 2 app to Django-powered website through Django Rest API.
The API is working and tested through Postman but I always get HTTP 400 BAD Request error in Ionic.
Here is my code in Ionic:
openCamera(){
var options = {
sourceType: Camera.PictureSourceType.CAMERA,
destinationType: Camera.DestinationType.DATA_URL
};
Camera.getPicture(options).then((imageData) => {
this.imageName = imageData;
this.imageURL = 'data:image/jpeg;base64,' + imageData;
}, (err) => {
this.showAlert(err);
});
}
Upload file (I am serving my Django project on my local PC with IP address 192.168.22.4):
transferData(auth){
let headers = new Headers();
headers.append('Authorization', auth);
let formData = new FormData();
formData.append('image', this.imageURL, this.imageName);
this.http.post("http://192.168.22.4/api-imageUpload", formData, {headers: headers}).subscribe(res => {
let status = res['status'];
if(status == 200){
this.showAlert( "The image was successfully uploaded!");
}else{
this.showAlert("upload error");
}
}, (err) => {
var message = "Error in uploading file " + err
this.showAlert(message);
});
}
On Django, here is my serializer:
class ImageDetailsSerializer(serializers.ModelSerializer):
image = serializers.ImageField(max_length=None, use_url=True)
class Meta:
model = ImageDetails
fields= ('image','status','category', 'user') ####status, category has default value
and views.py:
class ImageDetailsViewSet(generics.ListCreateAPIView):
queryset = ImageDetails.objects.all()
serializer_class = ImageDetailsSerializer
I am not sure if my code in uploading file is correct. I am trying to pass the data through Form data since the form works well in my API. Is this method correct? Are there any other methods to get this work?
Note: I have tried to use Transfer Cordova plugin but it is not working.
I finally solved the problem. The HTTP 400 indicates that there is a syntax error somewhere in the code and that is the encoding used in the uploaded photo. Mobile data uses base64 encoding. When sending requests, the file will then be converted to a Unicode string.
On the other hand, Django-Rest uses normal encoding for images, thus by default, it cannot support base64 image. But luckily, this plugin is already available at GitHub.
You just need to install the plugin and import it on your serializers.py:
from drf_extra_fields.fields import Base64ImageField
class ImageDetailsSerializer(serializers.ModelSerializer):
image = Base64ImageField()
class Meta:
model = ImageDetails
fields= ('image','status','category', 'user')
On Ionic side, you have to submit the actual image not the imageURL. In my case I just have to tweak my code to:
transferData(auth){
let headers = new Headers();
headers.append('Authorization', auth);
let formData = new FormData();
formData.append('category', 1);
formData.append('status', 'Y')
formData.append('image', this.imageName);
this.http.post("http://192.168.22.4/api-imageUpload", formData, {headers: headers}).subscribe(res => {
let status = res['status'];
if(status == 201){
var message = "The image was successfully uploaded!";
this.showAlert(message);
}else{
var message = "upload error";
this.showAlert(message);
}
}, (err) => {
var message = "Error in uploading file " + err;
this.showAlert(message);
});
In order to inspect what's ongoing with the request:
from rest_framework.exceptions import ValidationError
class ImageDetailsViewSet(generics.ListCreateAPIView):
queryset = ImageDetails.objects.all()
serializer_class = ImageDetailsSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
print(serializer.errors) # or better use logging if it's configured
raise ValidationError(serialize.errors)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Even without Base64 it is possible using ionic native components for file-transfer and image-picker see here: https://gist.github.com/AndreasDickow/9d5fcd2c608b4726d16dda37cc880a7b

how to stream file to client in django

I want to know how can I stream data to client using django.
The Goal
The user submits a form, the form data is passed to a web service which returns a string. The string is tarballed (tar.gz) and the tarball is sent back to the user.
I don't know what's the way. I searched and I found this, but I just have a string and I don't know if it is the thing I want, I don't know what to use in place of filename = __file__ , because I don't have file - just a string. If I create a new file for each user, this won't be a good way. so please help me. (sorry I'm new in web programming).
EDIT:
$('#sendButton').click(function(e) {
e.preventDefault();
var temp = $("#mainForm").serialize();
$.ajax({
type: "POST",
data: temp,
url: 'main/',
success: function(data) {
$("#mainDiv").html(data.form);
????
}
});
});
I want to use ajax, so what should i do in success of ajac function and in return of view. really thanks.
my view.py:
def idsBackup(request):
if request.is_ajax():
if request.method == 'POST':
result = ""
form = mainForm(request.POST)
if form.is_valid():
form = mainForm(request.POST)
//do form processing and call web service
string_to_return = webserviceString._result
???
to_json = {}
to_json['form'] = render_to_string('main.html', {'form': form}, context_instance=RequestContext(request))
to_json['result'] = result
???return HttpResponse(json.dumps(to_json), mimetype='application/json')
else:
form = mainForm()
return render_to_response('main.html', RequestContext(request, {'form':form}))
else:
return render_to_response("ajax.html", {}, context_instance=RequestContext(request))
You can create a django file instance of ContentFile using a string content instead of actual file and then send it as a response.
Sample code:
from django.core.files.base import ContentFile
def your_view(request):
#your view code
string_to_return = get_the_string() # get the string you want to return.
file_to_send = ContentFile(string_to_return)
response = HttpResponse(file_to_send,'application/x-gzip')
response['Content-Length'] = file_to_send.size
response['Content-Disposition'] = 'attachment; filename="somefile.tar.gz"'
return response
You can modify send_zipfile from the snippet to suit your needs. Just use StringIO to turn your string into a file-like object which can be passed to FileWrapper.
import StringIO, tempfile, zipfile
...
# get your string from the webservice
string = webservice.get_response()
...
temp = tempfile.TemporaryFile()
# this creates a zip, not a tarball
archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
# this converts your string into a filelike object
fstring = StringIO.StringIO(string)
# writes the "file" to the zip archive
archive.write(fstring)
archive.close()
wrapper = FileWrapper(temp)
response = HttpResponse(wrapper, content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=test.zip'
response['Content-Length'] = temp.tell()
temp.seek(0)
return response