I have a Spring Boot Rest application, and I want to pass a file from this to a Vue.js frontend with axios.
Firstly I get some data from Google Cloud Storage and send it to my frontend like this:
Get from gcloud:
public ByteArrayResource downloadObject(String bucketName, String objectName) {
BlobId blob = BlobId.of(bucketName, objectName);
return new ByteArrayResource(storage.readAllBytes(blob));
}
Send with Spring boot:
#RequestMapping(value = "/downloadFile", method= RequestMethod.GET)
public ResponseEntity<?> downloadFile(#RequestParam("fileRef") String fileRef){
String filename = "robot.png";
ByteArrayResource file = cloudStorage.downloadObject("bucket", "object");
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.contentLength(file.contentLength()).contentType(MediaType.APPLICATION_OCTET_STREAM).body(file);
}
Then I get the file in my frontend and try to save it, but the file is always corrupt.
Axios Request:
async downloadFile(fileRef){
await ApiService.downloadFile(fileRef).then( (res) => {
var blob=new Blob([res.data], {type: res.headers["content-type"]});
var fileDownload = require('js-file-download');
fileDownload(blob, 'robot.png');
}).catch((errr) => {
console.log(errr);
});
}
At the minute I've only tested this with a PNG file of a little robot, but I intend to be able to download JPGs, PDFS and MP3s as well. Any help would be massively appreciated.
I've also noticed that the headers I define in the Spring Boot response aren't visible from the frontend.
Worked it out, I need to put responseType: 'blob' in my axios request.
Related
For some reason, I don't want to share the URL public (sercet url),
My workflow is as below:
Client send API request to my server:
Api: mywebsite.com/api/image_abc.jpg
I have Nodejs express server to fetch the file from the url:
Eg: sercret_url.com/image_abc.jpg
And then from response image content from Nodejs, I send back the image content to the client and display as image_abc.jpg
I looked around on stackoverflow, but just got an answer from reading file from disk and send to client. What I want is just redirect the image content to client, not saving file to the disk.
Thank you.
Assuming you want to return the contents of a file from a certain URL to the client as buffer here's the solution I suggest
Get the file using axios and return the buffer to you client
const axios = require('axios');
let URL='some-valid-url'
const response = await axios.get(
URL,
{ responseType: 'arraybuffer' }
);
const buffer = Buffer.from(response.data, 'utf-8');
res.status(200).send(buffer);
In case you want to save it to your server you can use fs as follows to write the file to the folder of your choice
fs.writeFile(fileName, buffer, (err) => {
if(!err) console.log('Data written');
});
We have a Spring Boot java Backend and a frontend in Vue.js. Backend API code calls an external API to get MP3 and Vue.js frontend calls backend API to get the mp3 file and play the same.
Now sometimes the browsers are showing erratic behavior where sometimes they don't play the mp3 because of CORS issue.
Because of this, I want to download the file in the backend itself and return may be bytes to the frontend.
I am very new to the frontend and wondering what would be the best way to download the mp3 from backend and in which format the mp3 file should be sent to frontend and how to convert the bytes to mp3 using javascript(vue).
Please note the files size is extremely small. The file is <5sec mp3 file, so I don't want to store the file in the server.
From Spring you can return an array of bytes:
File f = new File("/path/to/the/file");
byte[] file = Files.readAllBytes(f.toPath());
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Disposition", "attachment; filename=\"" + f.getName() +".wav\"");
ResponseEntity<byte[]> response = new ResponseEntity(file, headers, HttpStatus.OK);
return response;
and then get it in javascript like so:
import fileDownload from 'js-file-download';
const { data } = await axios.get(`http://api.url`, {
responseType: 'arraybuffer',
headers: { 'Accept': '*/*', 'Content-Type': 'audio/wav' }
}).then(resp => resp);
const blob = new Blob([data], {
type: 'audio/wav'
})
const url = URL.createObjectURL(blob);
fetch(url)
.then(res => res.blob())
.then(blob => fileDownload(blob, 'your_filename'))
.catch(e => console.log('ERROR DOWNLOADING AUDIO FILE'));
I used React and axios, but I hope you'll manqge ;)
I am trying to save a file on the client side of my react application.
The data is obtained from my API Controller called DownloadDocument.
I am returning FileSreamResult from my DownloadDocument method.
FileStreamResult reads the data and writes it to the response in the react API.
The network tab gets a response, but when i log the response i get a response of null.
I am using file-saver to try and save the file. https://www.npmjs.com/package/file-saver.
Does anyone have any recommendations on what the issue is or if there is a better way of doing?
My action in the controller:
The issue that i am having is that my response for the react api is coming back null.
[HttpGet("{documentId}")]
[AcceptVerbs("OPTIONS", "GET")]
[AllowAnonymous]
public async Task<IActionResult> DownloadDocument(int documentId)
{
if (documentId != 0)
{
var document = await service.DownloadDocumentAsync(documentId);
var documentResponse = File(document, "application/octet-stream");
return documentResponse;
}
return BadRequest("Document id is not valid");
}
react application.
api.indexes.downloadDocument(clones)
.then(response=>{
console.log(response)
let FileSaver = require('file-saver');
let blob = new Blob([response], {type: "application/octet-stream"});
let filename ="testdownload"
FileSaver.saveAs(blob, filename)
Thanks for the help.
I needed to add this to the header.
responseType: 'blob'
this article explains it well
https://medium.com/#fakiolinho/handle-blobs-requests-with-axios-the-right-way-bb905bdb1c04
I'm using a spring boot backend and my api uses a service to send data via an OutputStreamWriter. I can download this in Angular 2 using a click event like so:
Typescript
results(){
window.location.href='myapicall';
}
HTML
<button (click)="results()"
class="btn btn-primary">Export</button>
This works just fine; however, I recently implemented security for my api endpoints and now I am receiving a 401 everytime I try to make the call because it's not sending a header.
I wrote a service that I can see the results in the console, but I can't seem to figure out how to download the file.
DownloadFileService
import {Injectable} from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/Rx';
#Injectable()
export class DownloadFileService {
headers:Headers;
bearer: string;
constructor(public http: Http) {}
getFile(url:string) {
this.bearer = 'Bearer '+ localStorage.getItem('currentUser');
this.headers = new Headers();
this.headers.append('Authorization', this.bearer);
return this.http.get(url, {headers: this.headers});
}
}
I tried downloading the data via a blob as suggested in this post:
How do I download a file with Angular2
The file that gets downloaded is of type File and the content is:
Response with status: 200 OK for URL:my url
It doesn't actually download the data.
downloadFile(data: any){
var blob = new Blob([data], { type: 'text/csv' });
var url= window.URL.createObjectURL(blob);
window.open(url);
}
results(){
// window.location.href='myapicall';
let resultURL = 'myapicall';
this.downloadfileservice.getFile(resultURL).subscribe(data => this.downloadFile(data)),//console.log(data),
error => console.log("Error downloading the file."),
() => console.info("OK");
}
Looks like you just need to parse the body of the response i.e
let parsedResponse = data.text();
this.downloadFile(parsedResponse);
Also I would recommend you use FileSaver to download files as even in 2016 there does not seem to be a standard way to do this across browsers.
let blob = new Blob([data], { type: 'text/csv' });
saveAs(blob, "data.txt");
For a more in depth guide check here
I use FileSaver, too. If you have extension on client side, you can see that it will work properly for CSV files. You just need to add extension manually:
FileSaver.saveAs(res, 'export' + extension);
My controller in Grails first fetches the data from a remote service API, and the returned data is a String. Then I would like to have the data downloaded as a csv file in the browser. I came across a similar post on SO and stole the code for using the response as below:
String exportResults = dataService.getDataFromService()
response.setHeader "Content-disposition", "attachment; filename=data_export.csv"
response.contentType = 'text/csv'
response.outputStream << exportResults.getBytes() //getBytes() not portable
response.outputStream.flush()
But this does not trigger any download window in the browser. I was wondering why. I use AngularJS to make a POST request to the controller and resolve the promise as below (JavaScript code):
ExportService.exportData(some_params).then(function(data) {
$log.info('export promise resolved: ');
//window.open(data, '_blank', ''); //not working
}).catch(function(err) {
$scope.message = "failed to retrieve data from export service";
$log.error(err);
}).finally(function(complete) {
$scope.message = "Data export completed.";
});
You need to replace 'text/csv' with 'application/octet-stream' for response.contentType. The content type 'application/octet-stream' is used for a binary file.
Try adding this after response.outputStream.flush() and see if it works.
webRequest.renderView = false
You can even try looking here.