Upload file to server
One of the most fundamental procedures that every web developer should learn is how to upload a file from web browser page to remote server.
Many real life situations require implementation of this procedure
- Setting photo for customized user profile
- Uploading various documents to remote server storage
- Image server-side processing (e.g. rescaling) and sending it back to client
Basics
- Client side logic have to send HTTP POST request to remote server IP address with
- file encoded in MultiPartFile format
- HTTP headers defining file name, format, size, etc..
WEB browsers have inbuilt capability to do previous by utilizing HTTP tag <input type="file" ../>
.
- Server side logic should accept HTTP POST request and
- extract file binary from request body,
- read HTTP headers to know file name and format type
- do some post-processing (e.g. save file to storage),
- send HTTP response back to client to inform him about the outcome (e.g. if everything was OK or if some ERROR happened)
Server side - Spring
For server side we will use Spring Java framework well known for its ability to make complex things very simple :)
Let's create Spring controller and one endpoint capable to accept client's HTTP POST request.
@Controller
@RequestMapping(path = "/files")
public class FileController {
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity> handleFileUpload(@RequestParam("file") MultipartFile file) {
try {
System.out.printf("File name=%s, size=%s\n", file.getOriginalFilename(),file.getSize());
//creating a new file in some local directory
File fileToSave = new File("C:\\test\\" + file.getOriginalFilename());
//copy file content from received file to new local file
file.transferTo(fileToSave);
} catch (IOException ioe) {
//if something went bad, we need to inform client about it
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
//everything was OK, return HTTP OK status (200) to the client
return ResponseEntity.ok().build();
}
}
Annotation
@RequestMapping(path = "/files")
tells spring controller to publish/expose all endpoints to server's relative path /files.In this example, assuming your server is listening on IP port 8080 (Tomcat's default), http://localhost:8080/files is URL of endpoint where client should send POST requests.
Client side - React
Step 1
Supposing you've already created React project, now create service.jsx file to setup basic client HTTP connection parameters
import axios from 'axios';
class Service {
constructor() {
console.log("Service is constructed");
}
getRestClient() {
if (!this.serviceInstance) {
this.serviceInstance = axios.create({
baseURL: 'http://localhost:8080/',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
},
});
}
return this.serviceInstance;
}
}
export default (new Service());
where
- baseURL is URL of server,
- timeout is max timeout interval for axios server instance to establish connection with server. If server cannot be reached and this interval expires, axios will throw Timeout error exception.
- header 'Content-Type': 'application/json' will be sent within request informing server that client expects json formatted answer.
Note that service is implemented using Singleton pattern. It means that Service object instance will be unique: constructed only once and later reused for all calls.
axios javascript library is probably all you'll ever need if you intend to send HTTP requests from your web page.
Step 2
Create file-service.jsx file using new service to setup Java Script Promise object for file upload request
import service from './service.jsx';
export class FileService {
uploadFileToServer(data){
//returns Promise object
return service.getRestClient().post('/files', data);
}
}
Step 3
Now let's define UI design and functionalities starting with new React component. Create file fileUploader.jsx with following source
import React, { Component } from 'react';
import { FileService } from '../services/file-service.jsx';
export class FileUploader extends Component {
constructor() {
super();
this.fileService = new FileService();
}
handleUploadFile = (event) => {
const data = new FormData();
//using File API to get chosen file
let file = event.target.files[0];
console.log("Uploading file", event.target.files[0]);
data.append('file', event.target.files[0]);
data.append('name', 'my_file');
data.append('description', 'this file is uploaded by young padawan');
let self = this;
//calling async Promise and handling response or error situation
this.fileService.uploadFileToServer(data).then((response) => {
console.log("File " + file.name + " is uploaded");
}).catch(function (error) {
console.log(error);
if (error.response) {
//HTTP error happened
console.log("Upload error. HTTP error/status code=",error.response.status);
} else {
//some other error happened
console.log("Upload error. HTTP error/status code=",error.message);
}
});
};
render() {
return (
<div>
<input type="file" onChange={this.handleUploadFile} />
</div>
)
};
}
Step 4
Put new component inside React's top level component (App.js file)
import React, { Component } from 'react';
import {FileUploader} from './components/fileUploader.jsx';
export class App extends Component {
render(){
return (
<div>
<FileUploader />
</div>
);
}
}