I have created simple rest method to upload and add file via multipart in Spring Boot.
I don't have any #RequestBody so I'm quite astonished why the browser throws 415 -> "Unsupported Media Type" Content type 'null' not supported
The controller looks like:
#RestController
#RequestMapping(value = "/api/file")
public class FileController {
#Autowired
private FileServiceImpl fileService;
#Autowired
private UserRepository userRepository;
#PreAuthorize("hasAnyAuthority('CLIENT')")
#RequestMapping(value = "", method = RequestMethod.POST, headers = "content-type=multipart/*", produces = "application/json", consumes = MediaType.APPLICATION_JSON_VALUE)
public File uploadFile(#RequestParam("uploadedFile") MultipartFile file, HttpServletRequest httpRequest) {
Principal name = httpRequest.getUserPrincipal();
if (name.getName() == null) {
throw new RuntimeException("Brak sesji");
}
User userByLogin = userRepository.findUserByLogin(name.getName());
File f = null;
if (!file.isEmpty()) {
try {
f = new File(file.getOriginalFilename(), file.getBytes(), file.getName(), file.getContentType(), new Date(), userByLogin);
} catch (IOException e) {
e.printStackTrace();
}
}
if (f != null) {
fileService.uploadFile(f);
}
return f;
}
}
At the frontend looks like
<div>
<label>Dodaj załącznik</label>
<input type="file" files-model="file" >
<button ng-click="addFile()">Dodaj</button>
</div>
$scope.addFile = function () {
FileService.save($scope.file);
}
The fail looks like:
{"timestamp":1491988354597,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'null' not supported","path":"/api/file"}
you have to append your file in formData.
<div>
<label></label>
<input type="file" files-model="file" id="file" >
<button ng-click="addFile()">Dodaj</button>
</div>
$scope.addFile = function () {
var file = document.getElementById("file").files[0];
var formData = new FormData();
formData.append("file", file);
FileService.save(formData);
};
In FileService service you have to set headers: {Content-Type:undefined}
this should work!
Related
I'm new to both react.js and ASP.Net core 2.0. And now writing a project using ASP.Net core 2.0 as back end API and react.js as application interface (front end). I'd like to know how to upload file. I have tried as follow but in the Back end side the parameter value (IFromFile file) is always null. And it seems that file was not posted correctly. Here are my codes:
.Net core (API)
[HttpPost]
[Route("upload")]
public async Task Upload(IFormFile file)
{
if (file == null) throw new Exception("File is null");
if (file.Length == 0) throw new Exception("File is empty");
using (Stream stream = file.OpenReadStream())
{
using (var binaryReader = new BinaryReader(stream))
{
var fileContent = binaryReader.ReadBytes((int)file.Length);
// await _uploadService.AddFile(fileContent, file.FileName, file.ContentType);
}
}
}
React.js
handleClick(event){
event.preventDefault();
// console.log("handleClick",event);
var self = this;
var apiBaseUrl = axios.defaults.baseURL + "user/upload";
if(this.state.filesToBeSent.length>0){
var filesArray = this.state.filesToBeSent;
const reader = new FileReader();
for(var i in filesArray){
//console.log("files",filesArray[i][0]);
var file = filesArray[i][0];
axios.post(apiBaseUrl, {data: file});
}
alert("File upload completed");
}
else{
alert("Please select files first");
}
}
Please advise how can I solve the issue.
I have done the job as follow:
at .Net core 2.0 web api
using Microsoft.AspNetCore.Http;
I created a model class
namespace Marter_MRM.Models
{
public class FileUploadViewModel
{
public IFormFile File { get; set; }
public string source { get; set; }
public long Size { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public string Extension { get; set; }
}
}
And then I created a controller class and wrote the function as follow.
[HttpPost]
[Route("upload")]
public async Task<IActionResult> Upload(FileUploadViewModel model) {
var file = model.File;
if (file.Length > 0) {
string path = Path.Combine(_env.WebRootPath, "uploadFiles");
using (var fs = new FileStream(Path.Combine(path, file.FileName), FileMode.Create))
{
await file.CopyToAsync(fs);
}
model.source = $"/uploadFiles{file.FileName}";
model.Extension = Path.GetExtension(file.FileName).Substring(1);
}
return BadRequest();
}
And write api call function in react as follow:
handleUploadClick(event){
event.preventDefault();
var self = this;
var apiBaseUrl = axios.defaults.baseURL + "user/upload";
if(this.state.filesToBeSent.length>0){
var filesArray = this.state.filesToBeSent;
let f = new FormData();
for(var i in filesArray){
//console.log("files",filesArray[i][0]);
f = new FormData();
f.append("File",filesArray[i][0] )
axios.post(apiBaseUrl, f, {
headers: {'Content-Type': 'multipart/form-data'}
});
}
alert("File upload completed");
}
else{
alert("Please select files first");
}
}
It works perfect. Thanks!
This answer is true but I have problem with saving an image in my API so I change the method as you see and then work nice. You should set the parameter as [FromForm] in your API method
public async Task<IActionResult> Upload([FromForm]FileUploadViewModel model){...}
[Route("api/[controller]")]
[ApiController]
public class UploaderController : ControllerBase
{
[HttpPost]
public dynamic UploadJustFile(IFormCollection form)
{
try
{
foreach (var file in form.Files)
{
string path = Path.Combine(#"C:\uploadFiles");
using (var fs = new FileStream(Path.Combine(path, file.FileName), FileMode.Create))
{
file.CopyToAsync(fs);
}
UploadFile(file);
}
return new { Success = true };
}
catch (Exception ex)
{
return new { Success = false, ex.Message };
}
}
and in UI use this
uploadJustFile(e) {
e.preventDefault();
let state = this.state;
this.setState({
...state,
justFileServiceResponse: 'Please wait'
});
if (!state.hasOwnProperty('files')) {
this.setState({
...state,
justFileServiceResponse: 'First select a file!'
});
return;
}
let form = new FormData();
for (var index = 0; index < state.files.length; index++) {
var element = state.files[index];
form.append('file', element);
}
debugger;
axios.post('/api/uploader', form)
.then((result) => {
let message = "Success!"
if (!result.data.success) {
message = result.data.message;
}
this.setState({
...state,
justFileServiceResponse: message
});
})
.catch((ex) => {
console.error(ex);
});
}
In my case i simply missed to add multipart/form-data in my form.
If your controller is accepting uploaded files using IFormFile but you find that the value is always null, confirm that your HTML form is specifying an enctype value of multipart/form-data. If this attribute isn't set on the element, the file upload won't occur and any bound IFormFile arguments will be null.
Example:-
<form method="post" enctype="multipart/form-data" asp-controller="UploadFiles" asp-action="Index">
<div class="form-group">
<div class="col-md-10">
<p>Upload one or more files using this form:</p>
<input type="file" name="files" multiple />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Upload" />
</div>
</div>
</form>
Remove the multiple attribute on this input element to allow just a single file to be uploaded.
I have images on machine of server. I want to retrieve image and display it in UI using angularjs.
I convert image to byte array in RestController. In angularjs, I send a request to get byte array then display byte array as an image in html. But image do not show on web page. Below is my code:
#RequestMapping(value = "/images", method = RequestMethod.GET)
public ResponseEntity<byte[]> getProductImage(String fileName) {
byte[] result = null;
File serverFile;
try {
serverFile = productService.getProductImage(fileName);
BufferedImage bufferedImage = ImageIO.read(serverFile);
// get DataBufferBytes from Raster
WritableRaster raster = bufferedImage.getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
result = (data.getData());
} catch (IOException e) {
e.printStackTrace();
}
String mimeType = URLConnection.guessContentTypeFromName(fileName);
HttpHeaders headers = null;
if (mimeType != null) {
headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf(mimeType));
}
return new ResponseEntity<>(Base64.getEncoder().encode(result), headers, HttpStatus.OK);
}
angularjs
vm.getByte = function(fileName){
$http({
method: 'GET',
url: '/api/images',
params: {
fileName: fileName
}
}).success(function(success){
vm.imgFile = success;
});
};
html code
<div class="form-group">
<label>Image</label>
<div class="form-group has-avatar" ng-if="vm.Product.productImg">
<img ng-src="data:image/JPEG;base64,{{vm.imgFile}}" style="width: 200px;height: 130px;" />
current result:
result
You should fix code in java follow:
#RequestMapping(value = "/images", method = RequestMethod.GET)
public ResponseEntity<byte[]> getProductImage(String fileName) {
byte[] result = null;
File serverFile;
try {
serverFile = productService.getProductImage(fileName);
FileInputStream fi = new FileInputStream(serverFile);
result = Base64.getEncoder().encode(IOUtils.toByteArray(fi));
} catch (IOException e) {
e.printStackTrace();
}
String mimeType = URLConnection.guessContentTypeFromName(fileName);
HttpHeaders headers = null;
if (mimeType != null) {
headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf(mimeType));
}
return new ResponseEntity<>(result, headers, HttpStatus.OK);
}
really don't now what to do.
i have on my server xls file and i wont to download it from my client.
i actually do it but the file getting in some weird langue and absolutely broken.
client : angular
server: spring 4
Code
Client
function getXCEL() {
self.onXCELProcess = true;
AdminService.getXL(MotherService.getAppMember())
.then(
function (response) {
var file = new Blob([response], {type: 'application/excel'});
var isChrome = !!window.chrome && !!window.chrome.webstore;
var isIE = /*#cc_on!#*/false || !!document.documentMode;
var isEdge = !isIE && !!window.StyleMedia;
if (isChrome){
var url = window.URL || window.webkitURL;
var downloadLink = angular.element('<a></a>');
downloadLink.attr('href',url.createObjectURL(file));
downloadLink.attr('target','_self');
downloadLink.attr('download', 'yourReports.xls');
downloadLink[0].click();
}
else if(isEdge || isIE){
window.navigator.msSaveOrOpenBlob(file,'yourReports.xls');
}
else {
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
self.onXCELProcess = false;
},
function (error) {
console.error("error : "+error+" , error : "+JSON.stringify(error));
self.onXCELProcess = false;
}
)
}
function getXL(admin) {
return MotherService.callToServer({method: 'POST', url: URL + "orderxl", appMember: admin});
}
callToServer() method do request, with json body. and return the deferred.promise
for use of then.
server
#RequestMapping(value = "/"+Request.ADMIN_PATH+"/"+Request.ORDER_XL, method = RequestMethod.POST)
public void getFile(#RequestBody OrderXLRequest request, HttpServletResponse response) {
try {
File file = orderXLService.execute(request).getFile(); //get the file. already saved and ready to send
try{
InputStream inputStream = new FileInputStream(file);
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", "excelfilename.xlsx");
response.setHeader(headerKey, headerValue);
try {
FileCopyUtils.copy(inputStream, response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}catch(Exception e){
System.out.println("Exception in file download :"+e);
}
}catch (Exception e) {
e.printStackTrace();
}
}
thank you very much for any help or suggestion
I wanna achieve a simple task, which is to retrieve the binary image, and display it in my html
public class Artwork
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid artworkID { get; set; }
public string artworkName { get; set; }
public string artworkMimeType { get; set; }
public byte[] artworkMeta { get; set; }
public string artworkBase64String { get; set; }
}
Gets the artwork from DB
public Artwork GetArtwork(Guid id)
{
return _context.Artworks.SingleOrDefault(a => a.artworkID == id);
}
The API Controller
public IHttpActionResult Get(Guid id)
{
if (id == null)
{
return BadRequest();
}
var artwork = _repository.GetArtwork(id);
if (artwork == null)
return NotFound();
else
return Ok(artwork);
}
I've also used this method and it returns the data I want, but I still don't know how to use it to achieve my goal.
[HttpGet]
public HttpResponseMessage Get(Guid id)
{
HttpResponseMessage result = null;
try
{
var artwork = _repository.GetArtwork(id);
if (artwork == null)
{
result = Request.CreateResponse(HttpStatusCode.Gone);
}
else
{
// sendo file to client
byte[] bytes = artwork.artworkMeta ;
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(bytes);
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = artwork.artworkName;
}
return result;
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.Gone);
}
}
And here's my angular request
$scope.getCity = function (id) {
$http.get('/api/artwork/' + $RouteParams.id).success(function (response) {
$scope.artwork= response;
//I've seen dudes using Blob here, but I'm not sure how that works
});
}
My problem is my angular request and my html, how do I display the artwork without doing this:
<img ng-src="data:{{artwork.artworkartworkMimeType}};base64,{{artwork.artworkBase64String}}" class="img-responsive" />
This displays the image, but I don't like how clumsy it looks, and I'm gonna be working with audio files as well, so I need a clean and understandable way. Please help!
As you said, this can be done by using a blob.
First step is to set the content type to application/octet-stream in the api method
[HttpGet]
public HttpResponseMessage Get(Guid id)
{
HttpResponseMessage result = null;
try
{
var artwork = _repository.GetArtwork(id);
if (artwork == null)
{
result = Request.CreateResponse(HttpStatusCode.Gone);
}
else
{
// sendo file to client
byte[] bytes = artwork.artworkMeta ;
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(bytes);
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = artwork.artworkName;
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
}
return result;
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.Gone);
}
}
Then add the client request where you create a blob from the response. An url is then created for the blob which will be the source for the img
$scope.fileURL = '';
$http({
method: 'GET',
url: '/api/artwork/' + $RouteParams.id,
responseType: 'arraybuffer',
headers: {
'Access-Control-Allow-Origin': '*',
}
}).success(function (data, status, headers) {
headers = headers();
var contentType = headers['content-type'];
var blob = new Blob([data], { type: contentType });
//Create a url to the blob
$scope.fileURL = window.URL.createObjectURL(blob);
}).error(function (message) {
console.log(message);
});
Then bind url to the ngSrc
<img ng-src="{{fileURL}}" class="img-responsive" />
You could store image in binary format without encoding it to base64. Then it would be simpler to retrive image from DB.
In your asp controller:
[HttpGet]
public FileResult GetPhoto(int id) {
return File(_repository.GetArtwork(id).artworkMeta, "image/jpg");
}
And in angular view:
<img ng-src="/Home/GetPhoto/2" />
i have an issue with getting the conent-disposition information from an uploaded file. The file upload itself is just working fine. But the Content-Disposition is null thats why i dont know the name and type ofthe uploaded file. I am triggering the post via angularJs's $http service.
#Path("/myrest")
#Service
public class RestService<Repsonse> {
private static final String SERVER_UPLOAD_LOCATION_FOLDER = "C://Users/steven/Desktop/upload/";
/**
* Upload a File
*/
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
#FormDataParam("myForm") InputStream fileInputStream,
#FormDataParam("myForm") FormDataContentDisposition fileDetail
) {
if (fileDetail == null) {
System.out.println("form contentDispositionHeader is null");
// return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Response.Status.INTERNAL_SERVER_ERROR.toString()).build();
} else {
System.out.println("form: " + fileDetail.toString());
}
Random randomno = new Random();
// String filePath = SERVER_UPLOAD_LOCATION_FOLDER + contentDispositionHeader.getFileName();
// String filePath = SERVER_UPLOAD_LOCATION_FOLDER + "bla.png";
String filePath = SERVER_UPLOAD_LOCATION_FOLDER + "test" + randomno.nextInt(10000) + ".jpg";
// save the file to the server
saveFile(fileInputStream, filePath);
String output = "File saved to server location : " + filePath;
return Response.status(200).entity(output).build();
}
// save uploaded file to a defined location on the server
private void saveFile(InputStream uploadedInputStream,
String serverLocation) {
OutputStream outpuStream = null;
try {
outpuStream = new FileOutputStream(new File(serverLocation));
int read = 0;
byte[] bytes = new byte[1024];
outpuStream = new FileOutputStream(new File(serverLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
outpuStream.write(bytes, 0, read);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(outpuStream != null) {
uploadedInputStream.close();
}
if(outpuStream != null) {
outpuStream.flush();
outpuStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The angularJs part:
js:
var file = $scope.myFile;
that.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
fd.append('file', file);
return $http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
});
};
The html part:
<div ng-controller="fileUploadController">
input type="file" file-model="myFile"/>
<br>
<br>
Name: {{myFile.name}} <br>
Size: {{myFile.size}} <br>
Type: {{myFile.type}} <br>
<br>
<br>
<button ng-click="uploadFile();">Upload</button>
</div>
Uploading files with ajax is in XHR level 2 spec and not really supported by all browsers...
Prefer an approach like $.fileUpload ( https://blueimp.github.io/jQuery-File-Upload/ ) for sending file in an angular/ one-page application.
If you do know what you are going, just add file.name or file.fileName in another field of your FormData object and get it that way server-side.