xiongzhu 4 anni fa
parent
commit
30c5e5f1f7

+ 4 - 0
src/main/java/com/izouma/nineth/domain/Collection.java

@@ -37,6 +37,10 @@ public class Collection extends BaseEntity {
     @Convert(converter = FileObjectListConverter.class)
     private List<FileObject> pic;
 
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = FileObjectListConverter.class)
+    private FileObject model3d;
+
     @ApiModelProperty("铸造者")
     @Searchable
     private String minter;

+ 1 - 1
src/main/java/com/izouma/nineth/service/AssetService.java

@@ -289,7 +289,7 @@ public class AssetService {
                 .toUserId(toUser.getId())
                 .toAvatar(toUser.getAvatar())
                 .operation(reason)
-                .price("转赠".equals(reason) ? price : null)
+                .price("转赠".equals(reason) ? null : price)
                 .build());
 
         asset.setPublicShow(false);

+ 66 - 0
src/main/java/com/izouma/nineth/utils/FileUtils.java

@@ -3,13 +3,18 @@ package com.izouma.nineth.utils;
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.PosixFileAttributeView;
 import java.nio.file.attribute.PosixFileAttributes;
 import java.nio.file.attribute.PosixFilePermission;
 import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Optional;
 import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
 
 public class FileUtils {
 
@@ -200,5 +205,66 @@ public class FileUtils {
 
     }
 
+    public static void unzip(InputStream in, File destDir) throws IOException {
+        try {
+            unzip(in, destDir, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            unzip(in, destDir, Charset.forName("GB2312"));
+        }
+    }
+
+    public static void unzip(InputStream in, File destDir, Charset charset) throws IOException {
+        byte[] buffer = new byte[1024];
+        ZipInputStream zis = new ZipInputStream(in);
+        ZipEntry zipEntry = zis.getNextEntry();
+        while (zipEntry != null) {
+            File newFile = newFile(destDir, zipEntry);
+            if (zipEntry.isDirectory()) {
+                if (!newFile.isDirectory() && !newFile.mkdirs()) {
+                    throw new IOException("Failed to create directory " + newFile);
+                }
+            } else {
+                // fix for Windows-created archives
+                File parent = newFile.getParentFile();
+                if (!parent.isDirectory() && !parent.mkdirs()) {
+                    throw new IOException("Failed to create directory " + parent);
+                }
 
+                // write file content
+                FileOutputStream fos = new FileOutputStream(newFile);
+                int len;
+                while ((len = zis.read(buffer)) > 0) {
+                    fos.write(buffer, 0, len);
+                }
+                fos.close();
+            }
+            zipEntry = zis.getNextEntry();
+        }
+        zis.closeEntry();
+        zis.close();
+    }
+
+    public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
+        File destFile = new File(destinationDir, zipEntry.getName());
+
+        String destDirPath = destinationDir.getCanonicalPath();
+        String destFilePath = destFile.getCanonicalPath();
+
+        if (!destFilePath.startsWith(destDirPath + File.separator)) {
+            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
+        }
+
+        return destFile;
+    }
+
+    public static File findInDir(File dir, String ext) {
+        if (!(dir.exists() && dir.isDirectory())) return null;
+        for (File file : Optional.ofNullable(dir.listFiles()).orElse(new File[0])) {
+            String name = file.getName().toLowerCase();
+            if (name.endsWith(ext.toLowerCase()) && !file.isHidden()) {
+                return file;
+            }
+        }
+        return null;
+    }
 }

+ 28 - 5
src/main/java/com/izouma/nineth/web/FileUploadController.java

@@ -10,10 +10,10 @@ import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.util.TempFile;
 import org.bytedeco.javacv.FFmpegFrameGrabber;
 import org.bytedeco.javacv.Frame;
 import org.bytedeco.javacv.Java2DFrameConverter;
-import org.pngquant.PngQuant;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -26,10 +26,7 @@ import java.awt.image.BufferedImage;
 import java.io.*;
 import java.net.URLConnection;
 import java.text.SimpleDateFormat;
-import java.util.Base64;
-import java.util.Date;
-import java.util.Objects;
-import java.util.Optional;
+import java.util.*;
 import java.util.regex.Pattern;
 
 
@@ -182,4 +179,30 @@ public class FileUploadController {
 
         return new FileObject(file.getOriginalFilename(), url, thumbUrl, file.getContentType());
     }
+
+    @PostMapping("/3dModel")
+    public FileObject upload3dModel(@RequestParam("file") MultipartFile file) throws IOException {
+        if (!"zip".equalsIgnoreCase(FilenameUtils.getExtension(file.getOriginalFilename()))) {
+            throw new BusinessException("只能上传zip");
+        }
+        File destDir = TempFile.createTempDirectory(RandomStringUtils.randomAlphabetic(20));
+        com.izouma.nineth.utils.FileUtils.unzip(file.getInputStream(), destDir);
+        File fbxFile = com.izouma.nineth.utils.FileUtils.findInDir(destDir, ".fbx");
+        if (fbxFile == null) {
+            throw new BusinessException("找不到fbx文件");
+        }
+        File fbxDir = fbxFile.getParentFile();
+        String basePath = "fbx/"
+                + new SimpleDateFormat("yyyy-MM_dd-HH").format(new Date()) + "/"
+                + RandomStringUtils.randomAlphabetic(16);
+        List<String> urls = new ArrayList<>();
+        for (File listFile : fbxDir.listFiles()) {
+            if (!listFile.isHidden() && !listFile.isDirectory()) {
+                urls.add(storageService.uploadFromInputStream(new FileInputStream(listFile), basePath + "/" + listFile.getName()));
+            }
+        }
+        String fbxUrl = urls.stream().filter(s -> s.toLowerCase().endsWith(".fbx")).findAny()
+                .orElseThrow(new BusinessException("找不到fbx文件"));
+        return new FileObject(fbxFile.getName(), fbxUrl, null, "fbx");
+    }
 }

+ 5 - 2
src/main/vue/src/components/FileUpload.vue

@@ -1,13 +1,14 @@
 <template>
     <el-upload
         class="file-upload"
-        :action="uploadUrl"
+        :action="customUrl || uploadUrl"
         :on-success="onSuccess"
         :headers="headers"
         :file-list="fileList"
         :limit="filesLimit"
         :on-exceed="onExceed"
         :on-preview="onPreview"
+        :accept="accept || '*/*'"
         ref="upload"
     >
         <el-button type="primary" size="mini" slot="trigger"> 点击上传 </el-button>
@@ -62,7 +63,9 @@ export default {
         format: {
             type: String,
             default: 'string'
-        }
+        },
+        customUrl: {},
+        accept: {}
     },
     data() {
         return {

+ 3 - 1
src/main/vue/src/views/BlindBoxEdit.vue

@@ -189,6 +189,7 @@
     </div>
 </template>
 <script>
+import resolveUrl from 'resolve-url';
 export default {
     name: 'BlindBoxEdit',
     created() {
@@ -428,7 +429,8 @@ export default {
                 id: [{ required: true, message: '请选择作品' }],
                 total: [{ required: true, message: '请输入数量' }]
             },
-            cateogories: ['勋章', '收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他']
+            cateogories: ['勋章', '收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他'],
+            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel')
         };
     },
     methods: {

+ 12 - 1
src/main/vue/src/views/CollectionEdit.vue

@@ -29,6 +29,15 @@
                         ></object-upload>
                         <div class="tip">支持JPG、PNG、GIF、MP4,推荐长宽比1:1</div>
                     </el-form-item>
+                    <el-form-item prop="model3d" label="3D模型">
+                        <file-upload
+                            :limit="1"
+                            v-model="formData.model3d"
+                            :customUrl="customUrl"
+                            accept="application/zip"
+                        ></file-upload>
+                        <div class="tip">请将FBX文件与贴图打包成zip压缩包上传</div>
+                    </el-form-item>
                     <el-form-item prop="minterId" label="铸造者">
                         <minter-select
                             v-model="formData.minterId"
@@ -221,6 +230,7 @@
     </div>
 </template>
 <script>
+import resolveUrl from 'resolve-url';
 export default {
     name: 'CollectionEdit',
     created() {
@@ -418,7 +428,8 @@ export default {
             privelegeRules: {
                 detail: [{ required: true, message: '请填写内容' }],
                 remark: [{ required: true, message: '请填写说明' }]
-            }
+            },
+            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel')
         };
     },
     methods: {