I'm trying to find a way to download file from api without window.open().
I'd like to get instant download when calling the api.
Currently downloading .xls file generated by a rest api using window.open()
API Endpoint
#GetMapping("/applications/export")
#Timed
public ResponseEntity<byte[]> exportApplicationsList() {
log.debug("REST request to export applications list");
byte[] result = applicationService.generateApplicationsListAsExcel();
if (result == null) {
return ResponseEntity.status(500).build();
}
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd_MM_yyyy_HH_mm"));
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=liste_applications_" + date + ".xls")
.contentLength(result.length)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(result);
}
Service
/**
* Generate xls file from applications list.
*
* #param applications list of applications
*/
public byte[] generateApplicationsListAsExcel() {
log.info("Génération fichier xls de la liste des applications");
List<Application> applications = applicationRepository.findAll();
Collections.sort(applications);
try (InputStream is = new FileInputStream(ResourceUtils.getFile("classpath:jxls-templates/liste_applications_template.xls"))) {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
Context context = new Context();
context.putVar("applications", applications);
JxlsHelper.getInstance().processTemplate(is, os, context);
return os.toByteArray();
} catch (IOException e) {
log.error(e.toString());
}
} catch (IOException e) {
log.error(e.toString());
}
return null;
}
Invocation
exportApplicationsList(): void {
window.open('/api/applications/export');
}
You can return file as blob as response from backend and then use file-saver to download the file
this.http.get(`/api/applications/export`, params, { responseType: 'blob' })
.subscribe((resp: any) => {
saveAs(resp, `filename}.xlsx`)
});
Quick solution : window.location.href = url;
I used this file-saver, I think it will fulfill your needs.
this.filesService.getDownloadFile(id).subscribe(
data => {
importedSaveAs(data, name);
},
err => {
console.error(err);
});
For the backend:
#GetMapping("download-file/{id}")
public ResponseEntity<?> downloadFile(#PathVariable(value = "id") Long id) {
final Optional<FileEntity> file = fileRepository.findById(id);
if (!file.isPresent()) {
return ResponseEntity.badRequest().body(getErrorResponse("File not found"));
}
ByteArrayOutputStream downloadInputStream = amazonClient.downloadFile(file.get().getLink());
return ResponseEntity.ok()
.contentType(contentType(file.get().getName()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.get().getName() + "\"")
.body(downloadInputStream.toByteArray());
}
Related
This is my controller end point that will take the request from the frontend with the filename and using that filename image/file shall be returned. It works fine while testing on postman but I have no idea how to load images/files from this end point in android.
GetMapping("downloadFile/{fileName}")
#PreAuthorize("hasRole('ADMIN') or hasRole('MODERATOR') or hasRole('USER')")
public ResponseEntity<Resource> downloadFile(#PathVariable String fileName, HttpServletRequest request) {
Resource resource = fileStorageService.loadFileAsResource(fileName);
//System.out.println(resource);
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException ex) {
ex.printStackTrace();
}
if (contentType == null) {
contentType = AppConstants.DEFAULT_CONTENT_TYPE;
}
return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
String.format(AppConstants.FILE_DOWNLOAD_HTTP_HEADER, resource.getFilename()))
.body(resource);
}
And My service for this looks like..
public Resource loadFileAsResource(String fileName) {
try {
//Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Path filePath= this.fileStoragePath.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
return resource;
} else {
throw new FileStorageException(AppConstants.FILE_NOT_FOUND + fileName);
}
} catch (MalformedURLException ex) {
throw new FileStorageException(AppConstants.FILE_NOT_FOUND + fileName, ex);
}
}
I need to load image/files from here to the android frontend.
Picasso is the option to do this. just load your images with the resource you receive from server...
I am trying to store a file from user, using Java Spring boot and make a preview immediately after image upload. But now I get a 404 error on the console and when I refresh the page, the image shows up!
I tried to solve the problem by adding sleep or delay in the thread. Did not work. Here is some part of controller and service file:
public Resource getIllustrationAsResource(long id) throws MalformedURLException {
return new FileUrlResource(uploadLocation + id + ILLUSTRATION_FILE_ENDING);
}
public PostResult addIllustration(MultipartFile file) throws IOException {
Illustration illustration = getIllustrationMetadata(file);
long id = repo.insertIllustration(illustration);
storeFile(file, id);
return new PostResult(id);
}
private void storeFile(MultipartFile file, long id) throws IOException {
Path path = Paths.get(uploadLocation + id + ILLUSTRATION_FILE_ENDING);
System.out.println("storeFile path: " + path);
File uploadFile = new File(uploadLocation);
uploadFile.getParentFile().mkdirs();
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, path);
}
}
Controller:
#GetMapping("/illustrations/{id}/image")
public ResponseEntity < Resource > getImageContent(#PathVariable("id") long id)
throws MalformedURLException {
Illustration illustrationInfo = service.getOneIllustration(id);
Resource body = service.getIllustrationAsResource(id);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(illustrationInfo.getMime()));
return new ResponseEntity < > (body, headers, HttpStatus.OK);
}
#PostMapping("/illustrations")
public PostResult postOneIllustration(#RequestParam("file") MultipartFile image)
throws IOException {
ProjectId project = projectService.getCurrentProject();
if (project == null) {
throw new EntityMissingException();
}
if (!authorizationService.loggedInUserCanCreateObjectsInProject(project)) {
throw new EntityMissingException();
}
return service.addIllustration(image);
}
Frontend:
postFile: async function(data) {
//console.log("here")
try {
let fetchPromise = catchReject(
fetch(`${window.contextPath}/illustrations`, {
method: 'POST',
body: data
})
)
const response = await fetchPromise
validateHttpOkStatus(response)
return response.json()
} catch (e) {
console.log('in postFile')
console.log(e)
// store.dispatch(addGlobalError(e))
throw e
}
},
I have A big file and i want to upload that in Server side. it's very important when occured any problem (like interrupting the internet or power cut ...) if i retry to upload, file uploaded from resume and doesn't need to send file from beginning.
I try this approach with sending file chunks but it seems that's not a good way, because a send chunks(byte arrays) directly in response Entity and this isn't good idea.
whatever if anybody can develop this approach and make this code a better code with better performance i appreciate that. does anybody known Best practice way to doing that??
and if u like my code, vote me
thanks :)
RestController
#RestController
#RequestMapping("/files")
public class Controller {
#Autowired
private MyService service;
#PutMapping("/upload/resume")
public Mono<ResponseEntity> uploadWithResume(#RequestPart("chunk")byte[] chunk,
#RequestPart("fileName")String fileName,
#RequestParam("length")Long length
) throws ParseException {
try {
return service.fileResumeUpload(chunk, fileName, length);
} catch (IOException e) {
e.printStackTrace();
return Mono.just(ResponseEntity.status(HttpStatus.PERMANENT_REDIRECT).build());
}
}
#RequestMapping(value = "/get/uploaded/size", method = RequestMethod.HEAD)
public Mono<ResponseEntity> getUploadedSize(#RequestParam("fileName") String fileName) throws IOException {
if (Files.exists(Paths.get("src/main/resources/" + fileName))) {
String size = String.valueOf(Files.size(Paths.get("src/main/resources/" + fileName)));
return Mono.just(ResponseEntity.ok()
.header("upload-offset", size)
.build());
} else{
return Mono.just(ResponseEntity.notFound()
.header("upload-offset" , "0").build());
}
}
}
Service
public Mono<ResponseEntity> fileResumeUpload(byte[] chunk , String fileName,long length) throws IOException, ParseException {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("src/main/resources/" + fileName, true));
boolean uploaded = true;
try {
out.write(chunk);
} catch (IOException e) {
uploaded = false;
System.err.println("io exception");
} finally {
if (uploaded) {
out.close();
return Mono.just(ResponseEntity.ok()
.header("expiration-date", getExpirationDate())
.build());
} else {
out.close();
return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
}
}
}
Sending chunks with webTestClient
#Test
public void test1_upload_Expected_200StatusCode(){
try {
String fileName = "film.mkv";
RandomAccessFile raf = new RandomAccessFile(new File("src/test/resources/" + fileName), "rw");
long realSize = raf.length();
List<String> strings = webTestClient.head().uri("/files/get/uploaded/size?fileName=" + fileName)
.exchange().expectBody().returnResult().getResponseHeaders().get("upload-offset");
long uploadedSize = Long.valueOf(strings.get(0));
boolean f = false;
int sizeBuffer = 256 * 1024;
byte[] buffer = new byte[sizeBuffer];
MultiValueMap<String, Object> formData;
WebTestClient.ResponseSpec exchange = null;
System.out.println("first uploaded Size ; " + uploadedSize);
raf.seek(uploadedSize);
while (raf.read(buffer) != -1) {
formData = new LinkedMultiValueMap<>();
formData.add("fileName", fileName);
formData.add("chunk", buffer);
formData.add("length", realSize);
exchange = webTestClient.put().uri("/files/upload/resume")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(formData))
.exchange();
exchange.expectStatus().isOk();
if (exchange.expectBody().returnResult().getStatus().is5xxServerError()) {
return;
}
if (uploadedSize + 256 * 1024 > realSize) {
sizeBuffer = ((int) (realSize - uploadedSize));
System.out.println(sizeBuffer);
uploadedSize = uploadedSize + sizeBuffer;
System.out.println(uploadedSize);
buffer = new byte[sizeBuffer];
f=true;
} else uploadedSize = uploadedSize + sizeBuffer;
if (f) System.out.println(uploadedSize);
//System.out.println(uploadedSize);
float percent = ((float) uploadedSize / realSize * 100);
System.out.format("%.2f\n", percent);
}
if (exchange!=null)
exchange.expectStatus().isOk();
}
catch (Exception e){
e.printStackTrace();
System.err.println("channel closed!!!");
}
}
I used a jersey server and I want that a endpoint redirect to the download of a file depending on parameters.
I have difficulties with the function below :
#GET
#Path("/get/{id}/{chunk}")
public Response getDescription(#PathParam("id") String id, #PathParam("chunk") String chunk) {
{
StreamingOutput fileStream = new StreamingOutput()
{
#Override
public void write(java.io.OutputStream output, String id) throws IOException, WebApplicationException
{
try
{
if (Objects.equals(chunk, new String("init"))) {
java.nio.file.Path path = Paths.get("src/main/uploads/example/frame_init.pdf");
}
else {
java.nio.file.Path path = Paths.get("src/main/uploads/example/"+ id +".pdf");
}
byte[] data = Files.readAllBytes(path);
output.write(data);
output.flush();
}
catch (Exception e)
{
throw new WebApplicationException("File Not Found !!");
}
}
};
return Response
.ok(fileStream, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition","attachment; filename = myfile.pdf")
.build();
}
I have a problem with passing parameters to the function write. I have my parameters id and chunk by the endpoint but I can't use it in the write method because it implements StreamingOutput().
How I can handle it ? Thank you
For java, final keyword should solve your problem.
As updated code;
#GET
#Path("/get/{id}/{chunk}")
public Response getDescription(#PathParam("id") final String id, #PathParam("chunk") final String chunk) {
{
StreamingOutput fileStream = new StreamingOutput()
{
#Override
public void write(java.io.OutputStream output, String id2) throws IOException, WebApplicationException
{
try
{
if (Objects.equals(chunk, new String("init"))) {
java.nio.file.Path path = Paths.get("src/main/uploads/example/frame_init.pdf");
}
else {
java.nio.file.Path path = Paths.get("src/main/uploads/example/"+ id2 +".pdf");
}
byte[] data = Files.readAllBytes(path);
output.write(data);
output.flush();
}
catch (Exception e)
{
throw new WebApplicationException("File Not Found !!");
}
}
};
return Response
.ok(fileStream, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition","attachment; filename = myfile.pdf")
.build();
}
I am new to angular .. I hava java rest api which return CSV file in response as attachment as | "Content-Disposition", "attachment; filename=" | content-type :application/octet-stream
Now when i am calling this api from AngularJS using $http i am getting response.data ="" (blank)
I am using basic authorisation for security so I have to pass Header while calling calling API so can't use link click or new window open fro CSV download.
to test when i removed authorisation and hit the url in browser then CSV file is being downloaded.so no issue at server side .
I need help at angularjs side to download CSV file from web api response as attachment.
Here is my Java API Code
public class ServiceAPI {
#GET
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFileAsCSVFile(){
byte[] file=null;
try {
ArrayList<> List=new ArrayList<>();// data retrieved from DB
if(null != List){
file=convertJsonToCSV(new Gson().toJson(List));
}
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Response.ok(getBytes(file),MediaType.APPLICATION_OCTET_STREAM).header("Content-Disposition", "attachment; filename=" + "FileName.csv").build();
}
}
and Angular code :
app.controller('review', ['$scope', '$http', function ($scope, $http){
$scope.fromDate = new Date();
$scope.toDate = new Date();
$scope.minDate = new Date(
$scope.fromDate.getFullYear(),
$scope.fromDate.getMonth() - 2,
$scope.fromDate.getDate(),
$scope.toDate.getFullYear(),
$scope.toDate.getMonth() - 2,
$scope.toDate.getDate()
);
$scope.maxDate = new Date(
$scope.fromDate.getFullYear(),
$scope.fromDate.getMonth() - 2,
$scope.fromDate.getDate(),
$scope.toDate.getFullYear(),
$scope.toDate.getMonth() - 2,
$scope.toDate.getDate()
);
$scope.reviews = json;
function openSaveAsDialog(filename, content, mediaType) {
var blob = new Blob([content], {type: mediaType});
saveAs(blob, filename);
}
function callApi(url) {
// var dat=apiFactory.getServiceData(url);
// console.log(dat);
// apiFactory.getServiceData(url);
var responseType='arraybuffer';
var expectedMediaType='application/octet-stream';
$http.get(url, {
headers: {
accept: expectedMediaType
},
responseType:responseType,
cache: true,
transformResponse: function (data) {
var pdf;
if (data) {
pdf = new Blob([data], {
type: expectedMediaType
});
}
return {
response: pdf
};
}
}).then(function (data,status,headers) {
var filename='Preview.csv',
octetStreamMime = "application/octet-stream",
contentType;
headers = data.headers();
contentType = headers["content-type"] || octetStreamMime;
// openSaveAsDialog(filename, response.data, expectedMediaType);
if (navigator.msSaveBlob) {
var blob = new Blob([data], { type: contentType });
navigator.msSaveBlob(blob, filename);
} else {
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if (urlCreator) {
// Try to use a download link
var link = document.createElement("a");
if ("download" in link) {
// Prepare a blob URL
var blob = new Blob([data.data], { type: contentType });
var url = urlCreator.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
// Simulate clicking the download link
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
link.dispatchEvent(event);
} else {
// Prepare a blob URL
// Use application/octet-stream when using window.location to force download
var blob = new Blob([data], { type: octetStreamMime });
var url = urlCreator.createObjectURL(blob);
$window.location = url;
}
}
}
});
};
$scope.submit = function (fromDate, toDate) {
$scope.url = API_url;
var resp =callApi(($scope.url).split(" ").join("%20"));
console.log(resp);
};
},
]);
I have an example with spring MVC instead of JAX-RS (Jersey)
HTML:
<button ng-click="downloadPdf()" class="btn btn-primary">download PDF</button>
Angularjs controler:
$scope.downloadCsv = function () {
console.log("downloadCsv");
var fileName = "test.csv";
var a = document.createElement("a");
document.body.appendChild(a);
XxxxxxServiceCSV.downloadCsv().then(function (result) {
console.log("downloadCsv callback");
var file = new Blob([result.data], {type: 'application/csv'});
var fileURL = URL.createObjectURL(file);
a.href = fileURL;
a.download = fileName;
a.click();
});
};
Angularjs services:
angular.module('xxxxxxxxApp')
.factory('XxxxxxServiceCSV', function ($http) {
return {
downloadCsv: function () {
return $http.get('api/downloadCSV', { responseType: 'arraybuffer' }).then(function (response) {
return response;
});
}
};
});
Java code JAX-RS(spring MVC):
#RequestMapping(value = "/downloadCSV", method = RequestMethod.GET, produces = "application/csv")
public void demo(HttpServletResponse response) throws IOException {
List<String> names = new ArrayList<String>();
names.add("First Name");
names.add("Second Name");
names.add("Third Name");
names.add("Fourth Name");
BufferedWriter writer = new BufferedWriter(response.getWriter());
try {
response.setHeader("Content-Disposition", "attachment; filename=\"test.csv\"");
for (String name : names) {
writer.write(name);
writer.write(",");
}
writer.newLine();
} catch (IOException ex) {
} finally {
writer.flush();
writer.close();
}
}