Implementation of Spring4+CKEditor4 File Upload Function

The installation configuration of CKEditor can be easily documented. To correct some errors on the Internet, it is necessary to start the upload function of CKEditor and modify a hide flag in the CKEditor code.The CKEditor code downloaded from the official website is compressed, ugly, let alone modified.The hide flag defaults to true only because it requires some preconditions. This is easy to understand. Uploading is not only a client thing, but also a server to receive it. Similarly, browsing server files is also the same. It must be what the server returns.In fact, as long as the filebrowserBrowseUrl and filebrowserUploadUrl parameters are set as follows, the browsing and uploading functions are automatically turned on without modifying the source file.And that's all in the documentation.
CKEDITOR.replace( 'editor2', {
         filebrowserBrowseUrl: '/browser/browse.php?type=Files',
         filebrowserUploadUrl: '/uploader/upload.php?type=Files'
         };
However, CKEditor's official documents are only in English and don't understand much. CKFinder has been selling around the bend in file upload, so it takes a lot of effort to implement file browsing and uploading.File browsing is basically OK. Write a page, enumerate the graphics files on the server one by one to generate <img>tags, and then write an onclick function to return the link to the parent window. This part of the document is also detailed and will be done as soon as possible.But when you upload, you get into the mistake of thinking that form is used to submit edits to the server and to upload files to the server, it is submitted with an XMLHTTP request, but you haven't found this in the document for a long time, it takes a lot of time, and you try to respond to the text directly in Spring. My dad's KEditor shows his clothes in the uploaded file.The box of server response content is almost no height, the server side can't see anything when it returns, and it doesn't know where something went wrong and nothing can be done.I have to search on the Internet for other people's implementations, almost all of them are strust2. I can't understand it. But after a long time, I found that JSON is not common in other people's implementations. That is, it doesn't seem like a response to XMLHTTP. Finally, I found a PHP implementation in the document. I still can't understand it, but it is just a normal web page.Change the Spring server to a normal view, and finally see a change after uploading the server. The original upload dialog box had an iframe showing the server's response with scroll bars. This shows an error page, meaning to persuade the server not to allow this page to be displayed in the framework, dumb dad, other people's code, although not fully understood, but none of themThat means, check it out on the Internet, here is Spring A default setting after EnableWebSecurity, which is required to cancel
@Configuration
@EnableWebSecurity
public class WebSecurityConfig
    extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
           ...
           
           .headers()
                .frameOptions().sameOrigin()

Or set it to disable(), so after that, rewrite the php page to the thymeleaf template page following the document.As a result, the experiment was successful and the link returned by the server was automatically filled in the url text box.This also explains why CKEditor's iframe is so small, because if it's okay, there's no need to show anything inside.Once you know the mechanism for uploading and echoing CKEditor, you're ready to go.The controller code is as follows:
 
@PostMapping
 public String handleFormUpload(@RequestParam("upload") Part file, @RequestParam("CKEditorFuncNum") String funcNum,
   Model model)   {
  model.addAttribute("funcnum", funcNum);
  String contenttype = file.getHeader("content-type");
  String filename = "";
  String ext = "";
  if (contenttype.equals("image/pjpeg") || contenttype.equals("image/jpeg"))
   ext = ".jpg";
  if (contenttype.equals("image/png") || contenttype.equals("image/x-png"))
   ext = ".png";
  if (contenttype.equals("image/gif"))
   ext = ".gif";
  if (contenttype.equals("image/bmp"))
   ext = ".bmp";
  if (contenttype.equals("image/x-icon"))
   ext = ".ico";
  if (contenttype.equals("image/pict"))
   ext = ".pic";
  if (contenttype.equals("image/tiff"))
   ext = ".tif";
  if (!ext.isEmpty()) {
   SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
   filename = sdf.format(new Date());
  }
  String path = context.getServletContext().getRealPath("/upload");
  try {
   InputStream inputStream = file.getInputStream();
   File save = new File(path, filename + ext);
   OutputStream outputStream = new FileOutputStream(save);
   byte[] buffer = new byte[1024];
   int length = 0;
   while ((length = inputStream.read(buffer)) > 0) {
    outputStream.write(buffer, 0, length);
   }
   inputStream.close();
   outputStream.close();
  } catch (IOException e) {
   model.addAttribute("data", "Upload Error!");
  }
  model.addAttribute("fileUrl", "\upload" + filename + ext);
  return "uploadresult";
 }

The view is as follows
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>File Upload Success</title>
</head>
<body>
 <script th:if="${data}==null" th:inline="javascript">
  window.parent.CKEDITOR.tools.callFunction([(${funcnum})], [[@{/}+${fileUrl}]]);
 </script>
 <script th:if="${data}!=null" th:inline="javascript">
  window.parent.CKEDITOR.tools.callFunction([(${funcnum})], [[@{/}+${fileUrl}]],[[${data}]]);
 </script>
</body>
</html>

This view has little to do but insert control data into the script using thymeleaf's script template functionality.CKEditor receives this page and displays it in an iframe, which is essentially executing a script that fills the url box with the file link returned by the server, which can then be inserted into the text being edited.
Finally, there is a problem with servlet upload files, which have a size limit that must be configured when configuring server upload capabilities.If you use Apache's Commons FileUpload component, you need to register a CommonsMultipartResolver Bean. If you use the MultipartResolver that comes with servlet 3.0, it is already included in the Dispatcher Servlet and you do not need to define beans, but when Dispatcher Servlet registers with a container, you must configure it, especially if you want to set a temporary directory to use it.The upload size limit is also set.
@Override
 protected void customizeRegistration(Dynamic registration) {
  registration.setMultipartConfig(new MultipartConfigElement("C:\\temp", 10485760, 10485760, 0));
 }

Although you can set the limit here very large, you still need to limit it. The problem is, what happens if the uploaded file size exceeds the limit and throws an exception, but this exception is very special and thrown by the servlet. If you program directly with the servlet, of course, there will be no problem. Catch and handle the exception, but now Sprin is used.G, Spring wraps the underlying servlet, then sends the request to the controller, and an exception is thrown before it is wrapped, so this exception cannot be caught and handled in the controller, that is, it cannot be caught in the controller with @ExceptionHandler(MultipartException.class).If you use Apache's Commons The FileUpload component can set a resolveLazily parameter value to defer exceptions until they are thrown in the controller, but no such parameter is available with the MultipartResolver that comes with Selvlet 3.0, only the values in the previous code can be set.In this case, only custom exception handlers are available.Similarly, this exception handler needs to be registered with the WebMvcConfigurerAdapter, otherwise there's nowhere to put it, either M or V or C.WebMvcConfigurerAdapter contains an extendHandlerExceptionResolvers method that can be used to extend exception handling, where custom exception handlers are added.In fact, there's nothing you can do with this exception, just respond to an error page and prompt for an error, so you don't have to implement any exception handling classes, just generate an instance of SimpleMappingExceptionResolver, and then associate the exception with the corresponding error page.The code is as follows:
 
@Override
 public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
  SimpleMappingExceptionResolver exceptionResolver=new SimpleMappingExceptionResolver();
  Properties mappings=new Properties();
  mappings.setProperty("MultipartException", "error");
  exceptionResolver.setExceptionMappings(mappings);
  exceptionResolver.setOrder(Ordered.LOWEST_PRECEDENCE);
  exceptionResolvers.add(exceptionResolver);
 }

Implementing a seemingly simple function involves so many points of knowledge: configuration of Spring upload components, X-Frame-Options and configuration, Spring exception handling, CKEditor and javascript scripts, and server-side save files.This is the time to generate the server side file name, because CKEditor submission server only has one Multpart, which could have taken the client side file name from the request header. First, it is cumbersome to get the file name. Second, if the client name is saved by the client name, the client name may be various and disorderly, third, it is easy to duplicate the name and troublesome to handle exceptions.Previously, this method was used to generate service-side file names, and if necessary, it should be improved.

Keywords: Spring PHP Thymeleaf Javascript

Added by wowiz on Wed, 22 May 2019 20:40:30 +0300